如何正确等待BackgroundWorker完成?
观察下面的代码:
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 处理程序运行的同一线程,这显然不是很明智的举动。
我想知道更好的解决方案。
谢谢。
编辑:
PS
- 示例代码是故意设计来澄清我的问题的。我完全知道完成回调,但我想知道如何等待完成。这是我的问题。
- 我知道
Thread.Join
、Delegate.BeginInvoke
、ThreadPool.QueueUserWorkItem
等...这个问题具体是关于BackgroundWorker
。
编辑2:
好的,我想如果我解释一下这个场景会容易得多。
我有一个单元测试方法,它调用一些异步代码,而这些代码最终又会调用一个 BackgroundWorker
,我可以将完成处理程序传递给它。所有代码都是我的,因此如果我愿意,我可以更改实现。 不过,我不会替换 BackgroundWorker
,因为它会自动使用正确的同步上下文,这样当在 UI 线程上调用代码时,就会在同一 UI 线程上调用完成回调,这非常好。
不管怎样,单元测试方法有可能在 BW 完成工作之前就结束了,这是不好的。所以我想等到 BW 完成并想知道最好的方法。
还有更多的部分,但总体情况或多或少像我刚才描述的那样。
Observe the following piece of code:
var handler = GetTheRightHandler();
var bw = new BackgroundWorker();
bw.RunWorkerCompleted += OnAsyncOperationCompleted;
bw.DoWork += OnDoWorkLoadChildren;
bw.RunWorkerAsync(handler);
Now suppose I want to wait until bw
finishes working. What is the right way to do so?
My solution is this:
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");
}
But I do not like it.
I have considered replacing the finished
flag with a synchronization event, set it in the RunWorkerCompleted
handler and block on it later instead of doing the while-sleep loop.
Alas, it is wrong, because the code may run in the WPF or WindowsForm synchronization context, in which case I would block the same thread as the RunWorkerCompleted
handler runs on, which is clearly not very smart move.
I would like to know of a better solution.
Thanks.
EDIT:
P.S.
- The sample code is so contrived intentionally to clarify my question. I am perfectly aware of the completion callback and yet I want to know how to wait till completion. That is my question.
- I am aware of
Thread.Join
,Delegate.BeginInvoke
,ThreadPool.QueueUserWorkItem
, etc... The question is specifically aboutBackgroundWorker
.
EDIT 2:
OK, I guess it will be much easier if I explain the scenario.
I have a unit test method, which invokes some asynchronous code, which in turn ultimately engages a BackgroundWorker
to which I am able to pass a completion handler. All the code is mine, so I can change the implementation if I wish to.
I am not going, however, to replace the BackgroundWorker
, because it automatically uses the right synchronization context, so that when the code is invoked on a UI thread the completion callback is invoked on the same UI thread, which is very good.
Anyway, it is possible that the unit test method hits the end before the BW finishes its work, which is not good. So I wish to wait until the BW completes and would like to know the best way for it.
There are more pieces to it, but the overall picture is more or less like I have just described.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
尝试像这样使用 AutoResetEvent 类:
警告:您应该确保无论发生什么情况都会调用
doneEvent.Set()
。此外,您可能希望为doneEvent.WaitOne()
提供指定超时时间的参数。注意:此代码几乎是 Fredrik Kalseth 回答 类似问题。
Try using the AutoResetEvent class like this:
Caution: You should make sure that
doneEvent.Set()
is called no matter what happens. Also you might want to provide thedoneEvent.WaitOne()
with an argument specifying a timeout period.Note: This code is pretty much a copy of Fredrik Kalseth answer to a similar question.
要等待后台工作线程(单个或多个),请执行以下操作:
创建您以编程方式创建的后台工作线程的列表:
在列表中添加后台工作人员:
使用以下函数等待列表中的所有worker:
To wait for a background worker thread (single or multiple) do the following:
Create a List of Background workers you have programatically created:
Add the background worker in the list:
Use the following function to wait for all workers in the List:
BackgroundWorker 有一个完成事件。不要等待,而是从完成处理程序中调用剩余的代码路径。
BackgroundWorker has a completion event. Instead of waiting, call your remaining code path from the completion handler.
这个问题很老了,但我认为作者没有得到他想要的答案。
这有点脏,它是在 vb.NET 中,但对我有用
This question is old but I don't think the author got the answer he was looking for.
This is a bit dirty, and it's in vb.NET but works for me
使用
Application.DoEvents()
在循环中检查backgrWorker.IsBusy
并不是一个好方法。我同意@JohannesH,你应该明确使用 AutoResetEvent 作为一个优雅的解决方案。但不要在UI Thread中使用,会导致主线程阻塞;它应该来自另一个后台工作线程。
Checking
backgrWorker.IsBusy
in the loop withApplication.DoEvents()
is not a nicely way.I agree with @JohannesH, you should definitively use AutoResetEvent as a elegant solution. But not using it in UI Thread, it will cause main thread blocked; it should come from another background worker thread.
VB.NET
您可以使用它来链接多个事件。 (接下来的伪代码)
VB.NET
You can use this to chain multiple events. (pseudo code to follow)
不太确定你所说的等待是什么意思。您的意思是您希望(由 BW)完成某件事,完成后您想做其他事情吗?
像您一样使用 bw.RunWorkerCompleted (使用单独的函数以提高可读性),并在该回调函数中执行下一步操作。
启动计时器来检查工作是否不会花费太长时间。
在 OnDoWorkLoadChildren 中:
not quite sure what u mean by waiting. Do you mean that you want something done (by the BW) after thats done you want to do something else?
Use bw.RunWorkerCompleted like you do (use a seperate function for readability) and in that callback function do you next stuff.
Start a timer to check if the work doesnt take too long.
In the OnDoWorkLoadChildren:
我也在寻找合适的解决方案。我用独占锁解决了等待。代码中的关键路径是写入公共容器(此处为控制台)并增加或减少工作人员。写入此变量时任何线程都不应干扰,否则计数将不再得到保证。
I was also looking for a suitable solution. I solved the waiting with an exclusive lock. The critical path in code are writing to a public container (here the console) and increasing or decreasing the workers. No thread should interfere while writing to this variable, otherwise the count is not guaranteed anymore.
我将 任务 与 BackgroundWorker
您可以创建任意数量的任务并将其添加到任务列表。
Worker 将在添加任务时启动,如果在 Worker IsBusy 期间添加任务则重新启动,并在没有更多任务时停止。
这将允许您根据需要异步更新 GUI,而不会冻结它。
这对我来说是有效的。
现在您所需要做的就是创建一个新任务并将其添加到列表中。它将由工作人员按照其放入 List<> 的顺序运行。
I used Tasks with a BackgroundWorker
You can create any number of tasks and add them to a list of tasks.
The worker will start when a task is added, restart if a task is added while the worker IsBusy, and stop once there are no more tasks.
This will allow you to update the GUI asynchronously as much as you need to without freezing it.
This works as is for me.
Now all you need is to create a new Task and add it to the List<>. It will be run by the worker in the order it was placed into the List<>
OpenCV 中存在函数 WaitKey。 Ir 允许以这种方式解决这个问题:
In OpenCV exists function WaitKey. Ir allows solve this issue in that way: