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失败。预期:其中15。实际:其中10取代。不在客户端线程上调用回调
我错过了什么?
在单元测试中,我看到像
这样的行为------ Run test started ------
MainThread Id =21
Worker Thread Id =9
Callback Thread Id =9
在Wpf App中,这将是
MainThread Id =1
Worker Thread Id =14
Callback Thread Id =1
更新 根据贾斯汀的回答,进行了以下更改,现在测试通过了
SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext(control.Dispatcher));
for (int i = 0; i < 3; i++)
{
control.Dispatcher.Invoke(DispatcherPriority.Background,
new Action(delegate { }));
Thread.Sleep(50);
}
答案 0 :(得分:8)
对于您正在运行的不同上下文,行为是不同的。
当您调用bw.RunWorkerAsync()时,将捕获SynchronizationContext。这用于分派RunWorkerCompleted调用。
在WPF下,它将使用DispatcherSynchronizationContext,它将完成的调用编组回UI线程。在测试中,这种编组是不必要的,因此它仍然保留在后台工作线程上。
答案 1 :(得分:3)
我相信调用线程必须支持messagepumping(意思是STA公寓并且有一个关联的Dispatcher),因此后台工作者可以发布回调。如果没有,后台工作者没有选择,只能在自己的线程中执行回调。如果您想测试它,请参阅this link。
答案 2 :(得分:0)
我在我的代码中遇到了一个问题,用户关闭窗口导致了一个保存,然后使用BackgroundWorker来更新主窗口并且它没有运行RunWorkerCompleted,因为启动BackgroundWorker的线程已经终止了窗户关闭了。
我必须在主窗口的上下文中更改关闭窗口的保存运行,以便在BackgroundWorker完成后,它有一个要返回的线程。
答案 3 :(得分:0)
就我而言,我正在使用Windows窗体,并且控件没有 Dispatcher 属性(请参见no definition for dispatcher中的答案)。
如果我们使用 Dispatcher.CurrentDispatcher 而不是控件中的那个,则Gishu的解决方案也可以正常工作。
关于测试初始化:
// I am using a field Dispatcher _dispatcher
_dispatcher = Dispatcher.CurrentDispatcher;
然后在等待后台任务完成时:
_dispatcher.Invoke(DispatcherPriority.Background, new Action(delegate { }));
Thread.Sleep(50);