这个BackgroundWorker正在忙碌中,无法同时运行多个任务。

58

如果我点击一个启动后台工作器两次的按钮,就会出现这个错误。

This BackgroundWorker is currently busy and cannot run multiple tasks concurrently

我该如何避免这种情况?

4个回答

106

简单易懂:不要两次启动 BackgroundWorker。

你可以使用 IsBusy 属性来检查它是否已经在运行,所以只需要修改这段代码:

worker.RunWorkerAsync();

转换为:

if( !worker.IsBusy )
    worker.RunWorkerAsync();
else
    MessageBox.Show("Can't run the worker twice!");

更新:

如果您确实需要同时启动多个后台任务,可以简单地创建多个BackgroundWorker对象。


在某种程度上,我同意答案并不完全有用。如果您确实需要运行两个后台进程,那么收到错误消息而不是“无法运行两次工作程序”并不好。我认为对于每个任务运行新的backgroundWorkers或以某种方式排队作业的答案是更好的解决方案,这样就不必检查系统的其他部分是否已经启动了后台作业并采取行动。 - Thies
2
IsBusy 什么时候被清除?我本以为它会在 RunWorkerCompleted 事件触发之前被清除,但根据我的实验情况并不是这样。 - Woodrow Barlow
@omJohn8372,原帖问到“如何避免这个错误”?答案是“不要启动超过一次的后台工作线程”。 - Orion Edwards
1
如果您在后台工作器中使用TcpListeners遇到问题,我的建议是不要这样做。使用TcpListener上的异步方法,使所有事情都以异步方式运行并释放出主线程来执行其他任务。大多数异步方法都需要一个CancellationToken,您可以在任何时候使用它来关闭它们,如果您愿意的话。 - Orion Edwards
1
@OrionEdwards,感谢您的建议,这是一个好建议。我无法准确回忆起我是如何解决它的,但我确实解决了它,而不是仅仅丢弃旧对象并重新开始。我没有完全使用TcpListener上的异步方法,但我认为我应该这样做——我之前就遇到过它们,它们似乎是处理事情的简洁方式。 - omJohn8372
显示剩余3条评论

54

为每个要执行的操作创建一个新的BackgroundWorker对象。也就是说,不要这样做:

BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
for (int i; i < max; i++) {
   worker.RunWorkerAsync(i);
}

试一下这个:

for (int i; i < max; i++) {
   BackgroundWorker worker = new BackgroundWorker();
   worker.DoWork += new DoWorkEventHandler(worker_DoWork);
   worker.RunWorkerAsync(i);
}

这是一个很好的答案。如果需要,我们如何杀死所有的backgroundWorkers? - Allan Spreys
@VladSpreys 我不能仅用一条评论来讨论线程取消,但在这个网站上有一些非常好的帖子谈到了这个主题。 - Justin R.
@fatcat1111 谢谢。我理解如果我们保留对线程的引用,可以取消线程的想法。但是在这段代码片段中,我们没有保留对它的引用。 - Allan Spreys
6
如果您想要一个工作者的参考列表,那么在 for 循环之外声明一个 List<BackgroundWorker>,然后在循环中,在实例化工作者之后将其添加到列表中。然后,如果需要取消它们,请遍历该列表并按最适合您情况的方式取消每个工作者。 - Justin R.

6
我建议您将需要完成的任务排队。这样可以获得以下优点:
  • 您不必处理后台任务无法启动的问题,因为其他任务已经在运行。
  • 您不必担心通过为每个任务创建新的后台工作线程而使用过多的线程。
  • 最后,队列可以确保后台任务按照它们被创建/请求的顺序运行。
以下是一个示例实现:[链接已损坏]。我不确定线程安全的实现方式,并且一旦我解决了当前锁定问题,就会更新我的答案。

1
示例实现的链接已失效。 - maets
这个答案是正确的,但如果能够提供代码实现会更好,而不是只提供链接。 - JumpingJezza

2
虽然不是原始问题的情况,但这也可能是由于竞态条件引起的(我现在遇到了这种情况,并寻找答案),如果您正在使用后台工作者来实现某种生产者-消费者模式。
例如:
if (BckgrndWrkr == null)
{
    BckgrndWrkr = new BackgroundWorker(); 
    BckgrndWrkr.DoWork += DoWorkMethod;
    BckgrndWrkr.RunWorkerAsync();     
}
else if (!BckgrndWrkr.IsBusy)
{
    BckgrndWrkr.RunWorkerAsync();
}

在这种情况下,有一个竞态条件:第一个实例实例化了一个新的后台工作器,第二个实例到达else if并启动了后台工作器,在第一个实例到达if块的RunWorkerAsync之前,然后它就会抛出错误。
可以通过将锁添加到整个if + else if部分来避免这种情况发生。

2
请不要用这样的变量命名方式。 - DatBear

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接