2010-10-25 118 views
1

在我的课上,我使用了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(); 
} 

回答

4

只要这个代码在主线程上运行,那么就没有比赛。 BGW只能在RunWorkerCompleted事件处理程序完成运行时完成。在主线程重新进入消息循环之前,处理程序不能开始运行。

有另一种比赛虽然由其他条款引起的。你让BGW开始没有一个RunWorkerCompleted事件处理程序。现在它可以异步完成,因为它不会被阻塞。 始终认购的情况下,测试e.Cancelled知道发生了什么。

1

你可以只在构造函数中添加RunWorkerCompleted事件处理程序一次,也是一个布尔成员变量“重新启动”添加到类。然后你可以写if(IsBusy)restart = true并在你的处理程序中检查是否(重启)Run()。您可以将重新启动定义为易失性,以避免竞争情况。

,我认为这不是一个很好的做法,在您的情况添加和删除事件处理程序。

0

也许我只是没有足够的智慧来理解你的代码。但在我的世界里,我会建立一个Queue<Action>并填写所有必须完成的工作。

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

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