观察以下代码片段:
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 循环。 但是很遗憾,这是错误的,因为代码可能在 WPF 或 WindowsForm 同步上下文中运行,在这种情况下,我会阻塞与 RunWorkerCompleted
处理程序运行的同一线程,这显然不是很明智的举动。
我想知道更好的解决方案。
谢谢。
编辑:
P.S.
- 示例代码是故意编写的,以阐明我的问题。 我完全了解完成回调,但我想知道如何等待完成。 这就是我的问题。
- 我了解
Thread.Join
,Delegate.BeginInvoke
,ThreadPool.QueueUserWorkItem
等等,但问题特别涉及到BackgroundWorker
。
编辑2:
好吧,如果我解释一下场景,那么这会很容易。
我有一个单元测试方法,它调用一些异步代码,这些代码最终涉及一个 BackgroundWorker
,我可以向其中传递完成处理程序。 所有的代码都是我的,因此我可以根据需要更改实现方式。但是,我不打算替换 BackgroundWorker
,因为它会自动使用正确的同步上下文,所以当在 UI 线程上调用代码时,完成回调会在同一 UI 线程上调用,这非常好。
无论如何,单元测试方法可能会在 BW 完成工作之前结束,这不太好。 因此,我希望等待 BW 完成,并想知道最佳方法。
它还有更多组件,但整体情况大致如我刚才所描述的那样。
AutoResetEvent.WaitAll(new[] {doneEvent1, doneEvent2})
即可。 - JohannesHwhile(!finished...){...}
解决方案中。因此,该解决方案将始终抛出 TimedoutException 异常。据我所知,由于 BackgroundWorker 的性质,只能等待 DoWork 处理程序完成,而无法等待 RunWorkerCompleted 处理程序完成。此外,无法保证作业已启动,但可以通过超时值解决此问题。 - JohannesH