【发布时间】: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 将在主线程中运行?
更新:我修改了我的代码以使用Tasks 并使用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 线程创建后台工作程序。这通常直接从向导中完成。由于您的情况似乎并非如此,那么您可能使用了错误的工具来完成这项工作(或者至少您使用不正确)。
标签: c# multithreading backgroundworker