如何处理这种竞争条件?

时间:2010-10-25 12:57:52

标签: c# .net multithreading backgroundworker

在我班上我使用的是BackgroundWorker。在某些时候我需要取消正在进行的异步操作并立即启动另一个。代码如下。我不确定的一件事是,如果工作人员在将我的lambda分配给RunWorkerCompleted事件之前就完成了竞争条件。如果发生这种情况,我的lambda永远不会被调用。代码中的注释显示了这个地方。关于如何处理这个问题的任何意见?

感谢 康斯坦丁


if (this.worker.IsBusy)
{
    RunWorkerCompletedEventHandler f = null;

    f = (s, v) =>
    {
        this.RunWorkerCompleted -= f;
        this.worker.RunWorkerAsync();
    };

    // what if worker completes right before the following statement?
    this.worker.RunWorkerCompleted += f;
    this.worker.CancelAsync();
}
else
{
    this.worker.RunWorkerAsync();
}

3 个答案:

答案 0 :(得分:4)

只要此代码在主线程上运行,就没有竞争。 BGW只能在RunWorkerCompleted事件处理程序完成运行时完成。在主线程重新进入消息循环之前,处理程序无法开始运行。

还有另一种种族,由 else 子句引发。您让BGW在没有 RunWorkerCompleted事件处理程序的情况下启动。现在它可以异步完成,因为它不会被阻止。 总是订阅活动,测试e.Cancelled以了解发生了什么。

答案 1 :(得分:1)

您可以在ctor中添加一次RunWorkerCompleted事件处理程序,并在该类中添加一个bool成员变量'restart'。然后你可以写if(IsBusy)restart = true并在你的处理程序中检查是否(重启)Run()。您可以将restart定义为volatile,以避免在这种情况下出现竞争条件。

我认为在你的案例中添加和删除事件处理程序并不是一个好习惯。

答案 2 :(得分:0)

也许我只是不够聪明才能理解你的代码。但在我的世界里,我会建立一个Queue<Action>并填写所有必须完成的工作。

另一个线程(或BackgroundWorker)将查看此队列并按顺序处理队列中的所有作业(如我的answer here)。由于在循环中使用Thread.Sleep(1)的拉模式,这可能不是很优雅。

但这可以通过创建从BindingQueue<T>派生的Queue<T>并实现IBindingList来实现。所以你可以等待这样的事件,出列并调用Action直到队列为空并重新开始。