为什么BackgroundWorker在此unit testing中没有在正确的线程上调用RunWorkerCompleted?

backgroundWorker的重点是在耗时的任务之后更新UI。 该组件的工作方式与我的WPF应用程序中的广告相同

但是在我的测试中,调用线程上没有调用回调。

[Test] public void TestCallbackIsInvokedOnClientThread() { var clientId = Thread.CurrentThread.ManagedThreadId; int callbackThreadId = -1; var manualEvent = new ManualResetEventSlim(false); var someUIControl = new TextBox(); var bw = new BackgroundWorker(); bw.DoWork += (s,e) => e.Result = 5 ; // worker thread bw.RunWorkerCompleted += (s, e) => { try { callbackThreadId = Thread.CurrentThread.ManagedThreadId; //someUIControl.Text = callbackThreadId.ToString(); manualEvent.Set(); } catch (System.Exception ex) { Console.Out.WriteLine(ex.ToString()); } }; bw.RunWorkerAsync(); if (!manualEvent.Wait(5000)) Assert.Fail("no callback"); Assert.AreEqual(clientId, callbackThreadId); } 

结果消息:Assert.AreEqual失败。 预期:。 实际:。 在客户端线程上未调用回调

我错过了什么?

在unit testing中,我看到的行为就像

 ------ Run test started ------ MainThread Id =21 Worker Thread Id =9 Callback Thread Id =9 

在Wpf应用程序中,这将是

 MainThread Id =1 Worker Thread Id =14 Callback Thread Id =1 

更新:根据贾斯汀的回答,进行了以下更改,现在测试通过了

  • 在创建BackgroundWorker SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext(control.Dispatcher));之前SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext(control.Dispatcher));
  • 不是在线程之间使用事件来发送信号,而是模拟消息泵

 for (int i = 0; i < 3; i++) { control.Dispatcher.Invoke(DispatcherPriority.Background, new Action(delegate { })); Thread.Sleep(50); } 

对于您正在运行的不同上下文,行为是不同的。

当您调用bw.RunWorkerAsync()时,将捕获SynchronizationContext。 这用于调度RunWorkerCompleted调用。

在WPF下,它将使用DispatcherSynchronizationContext,它将完成的调用编组回UI线程。 在测试中,这种编组是不必要的,因此它保留在后台工作线程上。

我相信调用线程必须支持messagepumping(意思是,作为STA公寓并具有关联的Dispatcher),因此后台工作者可以发布回调。 如果没有,后台工作者没有选择,只能在自己的线程中执行回调。 如果要测试它,请参阅此链接 。

我在我的代码中遇到了一个问题,其中关闭窗口的用户导致了一个保存,然后使用BackgroundWorker更新主窗口并且它没有运行RunWorkerCompleted,因为启动BackgroundWorker的线程在窗口关闭时终止。

我不得不在主窗口的上下文中更改关闭窗口的保存运行,以便在BackgroundWorker完成后,它有一个要返回的线程。