为什么BackgroundWorker在此单元测试中没有在正确的线程上调用RunWorkerCompleted?

Gis*_*shu 5 .net c# multithreading

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);
 }
Run Code Online (Sandbox Code Playgroud)

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

我错过了什么?

在单元测试中,我看到的行为就像

------ Run test started ------
MainThread Id =21
Worker Thread Id =9
Callback Thread Id =9
Run Code Online (Sandbox Code Playgroud)

在Wpf应用程序中,这将是

MainThread Id =1
Worker Thread Id =14
Callback Thread Id =1
Run Code Online (Sandbox Code Playgroud)

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

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

.

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

Jus*_*vey 8

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

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

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