2012-05-17 54 views
2

我有一個後臺工作人員,需要基於複選框的數量被調用多次 - 我寫了這個來獲取複選框的值,並將它們放入ListRunWorkerAsync關閉完成的BackgroundWorker

List repSelected = new List();

這是填充,然後遍歷像這樣:

foreach (string rep in repSelected) 
{ 
    backgroundWorker1.RunWorkerAsync(rep); 
    backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted); 
}

異步,DoWork的代碼如下所示:

BackgroundWorker worker = sender as BackgroundWorker; 
string rep = e.Argument.ToString(); 

if (worker.CancellationPending == true) 
{ 
    e.Cancel = true; 
} 
else 
{ 
    DirectoryExists(rep); 
    ProcessRunner(rep); //Rars some large files - expensive 
}

過程然後運行WorkerComplete,問題是當該過程返回去執行Worker的下一次迭代,它崩潰時表示該worker正忙 - 即使worker已經返回它的WorkerCompleted狀態。

如何確保在循環的下一次迭代之前關閉線程?

注:我有一個包含!backgroundWorker1.IsBusy()的背景工作條件,但是這(顯然)只是跳過剩餘的迭代而不執行。

回答

1

如果要按順序處理每個項目,沒有理由爲每個任務使用單獨的背景工作。所以將foreach循環移動到DoWork方法會更好。但是,如果您想要以並列方式處理所有項目,則需要爲每個項目創建一個後臺工作人員。

+0

非常好,正是我之後,謝謝!出於好奇,是否有可能爲每個項目創建一個新的後臺工作者,但仍然使用相同的代碼來執行? –

+1

您可以將DoWork代碼移動到單獨的方法中,並使用rep值作爲參數。但是,在您的代碼中,似乎您正在處理文件,因此如果將它們放置在機械驅動器(而非SSD)上,那麼如果按順序處理文件,我認爲您的代碼的性能會更好。這是因爲您將避免來回移動磁盤頭。 –

+0

@HenkHolterman使用1 Backgroundworker with parallel.foreach將導致相同的異常。 – daryal

1

從MSDN:

如果後臺操作已經運行,再次調用的RunWorkerAsync會引發一個InvalidOperationException。

所以你不能使用BackgroundWorker維護任務的隊列(和你推所有任務的順序,而不用等待一個之前完成)。您有此不同的解決方案,例如,如果你想使用BackgroundWorker你可以這樣做是爲了保持:

backgroundWorker1.RunWorkerAsync(repSelected); 

然後改變你的DoWork方法類似如下:

BackgroundWorker worker = sender as BackgroundWorker; 

foreach (string rep in (IEnumerable<string>)e.Argument) 
{ 
    if (worker.CancellationPending == true) 
    { 
     e.Cancel = true; 
     return; 
    } 
    else 
    { 
     DirectoryExists(rep); 
     ProcessRunner(rep); //Rars some large files - expensive 
    } 
} 

作爲替代你可以考慮更改執行此任務的方式,例如使用System.Threading.Tasks.TaskThreadPool(直接或間接地將大多數並行操作排入池中)。

1

您的foreach代碼將立即觸發所有元素的工作。這就是爲什麼你會得到例外。

如果要按順序啓動工作程序,則只能在啓動時調用RunWorkerAsync一次,然後再爲每個WorkerComplete事件調用RunWorkerAsync。但爲什麼不在工人代碼中進行處理呢?

1

BackgroundWorker真的很忙,因爲當第一次調用backgroundWorker1.RunWorkerAsync(rep);時,它不會等待任何事情,那麼將立即調用第二,第三,...調用。

您應該爲每次調用創建BackgroundWorker,然後就可以了。

1

你可以使用parallel.foreach和多個backgroundworkers;

Parallel.ForEach(YourListofStrings, 
       (q) => 
       { 
        BackgroundWorker worker = new BackgroundWorker(); 
        worker.DoWork += new DoWorkEventHandler(worker_DoWork); 
        worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted); 
        worker.RunWorkerAsync(q); 
       }); 
+0

良好的解決方案 - 但我會採取Espen的建議,並按順序運行以避免抖動硬盤。 +1 –

相關問題