为啥 BackgroundWorker 的 Progress 事件运行在不同的线程上?
Posted
技术标签:
【中文标题】为啥 BackgroundWorker 的 Progress 事件运行在不同的线程上?【英文标题】:Why BackgroundWorker's Progress event runs on a different thread than it should?为什么 BackgroundWorker 的 Progress 事件运行在不同的线程上? 【发布时间】:2021-11-25 06:07:19 【问题描述】:我在使用 BackgroundWorker
时遇到问题。与文档相反,它似乎在与创建它的线程不同的线程中调用其 Progress 事件。首先,供参考:
这是在创建工人时:
这是,当工人在做它的工作时:
这是,当我的代码处理工作人员通过ReportProgress
报告的进度时:
注意,最后一个线程甚至与前两个不同(主线程和 BW 的工作线程)
worker 是在 App.xaml.cs 中间接创建的:
public App()
Configuration.Configure(Container.Instance);
// (...)
// Configure core service
coreService = Container.Instance.Resolve<ICoreService>();
coreService.Init();
然后:
public CoreService(ITfsService tfsService, IConfigurationService configurationService, IMapper mapper, IDialogService dialogService)
this.tfsService = tfsService;
this.configurationService = configurationService;
this.dialogService = dialogService;
worker = new CoreWorker(tfsService, configurationService);
worker.ProgressChanged += HandleCoreWorkerProgress;
Worker 在CoreService.Init()
中被执行:
public void Init()
tfsService.Connect();
worker.RunWorkerAsync();
Worker 本身看起来不像这样:
private class CoreWorker : BackgroundWorker
private const int CORE_WORKER_TIMEOUT = 1000;
private readonly ITfsService tfsService;
private readonly IConfigurationService configurationService;
// (...)
public CoreWorker(ITfsService tfsService, IConfigurationService configurationService)
this.tfsService = tfsService;
this.configurationService = configurationService;
this.WorkerSupportsCancellation = true;
this.WorkerReportsProgress = true;
protected override void OnDoWork(DoWorkEventArgs e)
while (true)
if (this.CancellationPending)
break;
if (EnsureTfsServiceUp())
(var startedBuilds, var succeededBuilds, var failedBuilds) = ProcessBuilds();
// Download and process builds
var buildResult = new BuildInfo(startedBuilds, succeededBuilds, failedBuilds);
ReportProgress(0, buildResult);
// Wait 1 minute
Thread.Sleep(CORE_WORKER_TIMEOUT);
注意,我使用异步方法,但我明确地等待它们(因为无论如何我都在一个单独的线程中):
(reachedSince, recentBuilds) = tfsService.GetBuilds(lastBuild.Value).Result;
runningBuilds = tfsService.GetBuilds(runningBuildIds).Result;
plannedBuilds = tfsService.GetBuilds(plannedBuildIds).Result;
我有什么遗漏吗? Docs 说,进度处理应该发生在与创建BackgroundWorker
时相同的线程中。但事实并非如此,而且我显然在 UI 上遇到了错误。
应用程序是 WPF,但是它在后台工作,没有主窗口,它的大部分工作都在 CoreService 中完成,它只是偶尔显示窗口。
我该如何解决这个问题,以便 ReportProgress 将在主线程中运行?
更新:我修改了我的代码以使用Task
s 并使用Progress<T>
来处理进度:
notificationTask = Task.Factory.StartNew(async () => await ProcessNotifications(new Progress<BaseCoreNotification>(HandleNotificationProgress), notificationTaskCancellationSource),
notificationTaskCancellationSource.Token,
TaskCreationOptions.LongRunning,
TaskScheduler.Default).Unwrap();
结果是主线程中仍未处理进度。
【问题讨论】:
RunWorkerAsync
方法在哪里调用?
@TheodorZoulias,在 CoreService.Init() 中,在从 DC 解决它之前排在前面。
引用this 答案:BackgroundWorker
在调用RunWorkerAsync()
的线程的当前SynchronizationContext
上引发ProgressChanged
事件。
附带说明,在 async-await 技术出现之后,BackgroundWorker
类已呈现为 technologically obsolete。
通常应该从(WinForms 应用程序的)UI 线程创建后台工作程序。这通常直接从向导中完成。由于您的情况似乎并非如此,那么您可能使用了错误的工具来完成这项工作(或者至少您使用不正确)。
【参考方案1】:
切换到Task
s后发现问题。
在这种情况下,要报告进度,我需要使用 Progress<T>
。这个类应该在构造它的线程中调用委托,但反过来它只是从池中获取一个线程。
原因是我的服务的初始化(在 Progress<T>
创建的位置)已经在 App
ctor 中完成,结果是,那里没有 SynchronizationContext
(即, SynchronizationContext.Current
为空)。在这种情况下,Progress<T>
只是使用池中的线程,而且 - 很可能 - BackgroundWorker
也这样做。
解决方案是将我的服务的初始化移动到Application_Startup
,其中主线程的SynchronizationContext
已经可用。
【讨论】:
以上是关于为啥 BackgroundWorker 的 Progress 事件运行在不同的线程上?的主要内容,如果未能解决你的问题,请参考以下文章
为啥相同的代码在我的 BackGroundWorker 线程中比在我的 GUI 线程中慢得多?
Flash Pro cc 为啥我手机上的舞台宽度和高度设置为Flash pro里面的?
C# form发起backgroundworker 当form close时 backgroundworker 还会继续工作吗