为什么 BackgroundWorker 的 Progress 事件运行在不同的线程上?

Why BackgroundWorker's Progress event runs on a different thread than it should?

我在使用 BackgroundWorker 时遇到问题。它似乎在与创建时不同的线程中调用其 Progress 事件,这与文档相反。一、供参考:

这是在创建 worker 时:

这是,当工人正在做它的工作时:

这是,当我的代码处理工作人员通过 ReportProgress:

报告的进度时

请注意,最后一个线程甚至与前两个(主线程和 BW 的工作线程)不同

工人是在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;

有什么我想念的吗?文档说,进度处理应该发生在与创建 BackgroundWorker 时相同的线程中。但事实并非如此,而且我显然在 UI.

上遇到了错误

应用程序是 WPF,但是它在后台运行,没有主程序 window,它的大部分工作是在 CoreService 中完成的,它只是偶尔显示 windows。

我该如何解决这个问题,以便 ReportProgress 运行 进入主线程?


Update:我修改了我的代码以使用 Tasks 代替并使用 Progress<T> 来处理进度:

            notificationTask = Task.Factory.StartNew(async () => await ProcessNotifications(new Progress<BaseCoreNotification>(HandleNotificationProgress), notificationTaskCancellationSource), 
                notificationTaskCancellationSource.Token, 
                TaskCreationOptions.LongRunning, 
                TaskScheduler.Default).Unwrap();

结果是进度还没有在主线程中处理

切换到Tasks后发现问题。

在那种情况下,要报告进度,我需要使用 Progress<T>。这个 class 应该在构造它的线程中调用委托,但反过来它只是从池中拿了一个线程。

原因是我的服务初始化(创建 Progress<T> 的地方)已经在 App ctor 中完成,结果 没有 SynchronizationContext那里(即SynchronizationContext.Current为空)。在这种情况下 Progress<T> 简单地使用池中的线程并且 - 很可能 - BackgroundWorker 也这样做。

解决方案是将我的服务初始化移动到 Application_Startup,其中主线程的 SynchronizationContext 已经可用。