观察以下代码:
var handler = GetTheRightHandler();
var bw = new BackgroundWorker();
bw.RunWorkerCompleted += OnAsyncOperationCompleted;
bw.DoWork += OnDoWorkLoadChildren;
bw.RunWorkerAsync(handler);
现在假设我要等到
bw
完成工作。正确的做法是什么?我的解决方案是这样的:
bool finished = false;
var handler = GetTheRightHandler();
var bw = new BackgroundWorker();
bw.RunWorkerCompleted += (sender, args) =>
{
OnAsyncOperationCompleted(sender, args);
finished = true;
});
bw.DoWork += OnDoWorkLoadChildren;
bw.RunWorkerAsync(handler);
int timeout = N;
while (!finished && timeout > 0)
{
Thread.Sleep(1000);
--timeout;
}
if (!finished)
{
throw new TimedoutException("bla bla bla");
}
但我不喜欢它。
我考虑过用同步事件替换
finished
标志,在RunWorkerCompleted
处理程序中设置它,然后在其上阻塞,而不是执行while-sleep循环。las,这是错误的,因为代码可能在WPF或WindowsForm同步上下文中运行,在这种情况下,我将阻塞与
RunWorkerCompleted
处理程序运行所在的线程相同的线程,这显然不是很明智的举动。我想知道一个更好的解决方案。
谢谢。
编辑:
P.S.
Thread.Join
,Delegate.BeginInvoke
,ThreadPool.QueueUserWorkItem
等等。问题特别是关于BackgroundWorker
。 编辑2:
好的,我想如果我解释这种情况,它将容易得多。
我有一个单元测试方法,该方法调用一些异步代码,而这些代码最终会使用
BackgroundWorker
,我可以将完成处理程序传递给该BackgroundWorker
。所有代码都是我的,因此我可以根据需要更改实现。但是,我不打算替换ojit_code,因为它会自动使用正确的同步上下文,以便在UI线程上调用代码时,在同一UI线程上调用完成回调。
无论如何,单元测试方法有可能在BW完成工作之前就结束了,这不好。因此,我希望等到BW完成后,并想知道实现此目标的最佳方法。
它有更多的部分,但总体情况或多或少像我刚才描述的那样。
最佳答案
尝试使用AutoResetEvent类,如下所示:
var doneEvent = new AutoResetEvent(false);
var bw = new BackgroundWorker();
bw.DoWork += (sender, e) =>
{
try
{
if (!e.Cancel)
{
// Do work
}
}
finally
{
doneEvent.Set();
}
};
bw.RunWorkerAsync();
doneEvent.WaitOne();
警告:不管发生什么情况,都应确保调用
doneEvent.Set()
。另外,您可能想为doneEvent.WaitOne()
提供一个指定超时期限的参数。注意:该代码几乎是Fredrik Kalseth对similar question的回答的副本。