2010-10-25 116 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,直到隊列爲空並重新開始。