2014-03-06 175 views
1

我產生了多個backgroundworkerthreads,並希望我的主線程等待,直到所有完成。解決方法是在每次生成BackgroundWorker線程時將項目添加到列表中,並在RunWorkerCompleted中將其刪除。但是,如何將列表作爲參數傳遞給RunWorkerCompleted?主線程等待多個backgroundworker線程來完成

FuncA() 
{ 
foreach() 
{ 
    /* add an item to the list */ 
    _bw.RunWorkerAsync(); 
} 
m_event.WaitOne(); /* Main thread waits here */ 
} 
static bw_DoWork() 
{ 

} 
static bw_RunWorkerCompleted() 
{ 
    /* delete item from list */ 
    /* if list is empty signal m_event.Set() */ 
} 
+0

你不想阻塞主線程。這會凍結你的用戶界面。另外,順便說一句,它會阻止'RunWorkerCompleted'發射。 – Servy

+0

你能解釋一下,如果主線程被阻塞,RunWorkerCompleted將不會被觸發嗎? – user759913

+0

BGW會將完成的事件封送到主線程,因爲它被阻塞,所以無法處理請求,直到當前請求結束。在BGW完成事件運行之前它不會完成。僵局。 – Servy

回答

1

使用TPL與await使得這個很容易

private void someEventHandler() 
{ 
    var results = await Task.WhenAll(
     Task.Run(() => ComputeSomeValue()), 
     Task.Run(() => ComputeSomeOtherValue()), 
     Task.Run(() => ComputeYetAnotherValue())); 
    DoSomethingWithResults(results); 
} 

對於.NET 4.0解決方案,您可以使用任務,而無需使用await

private void someEventHandler() 
{ 
    Task.Factory.ContinueWhenAll(new[]{ 
     Task.Run(() => ComputeSomeValue()), 
     Task.Run(() => ComputeSomeOtherValue()), 
     Task.Run(() => ComputeYetAnotherValue())} 
     , resultTask => DoSomethingWithResults(resultTask.Result); 
} 
+0

不幸的是,我的應用程序運行在.NET框架4.0中,並且不能支持TPL :(。由於其他依賴約束,我無法更改框架。是否有其他解決方法? – user759913

+0

當然,還有其他方法,儘管它們不是幾乎一樣好,我添加了一個可能性 – Servy

-1

它你使用.NET Framework 4或更高版本,您可以使用System.Threading.CountdownEvent。 在啓動後臺工作人員之前,您必須使用一些對象初始化CountdownEvent,並在WorkerCompleted處理程序中調用CountdownEvent的Signal()方法。 在所有工作者完成之前不應繼續的線程中,必須調用CountdownEvent的Wait()方法,然後在該方法之後調用您必須稍後執行的所有代碼。

+0

這會造成死鎖,你會在等待後臺工作人員的時候阻塞UI線程,當RunWorkerCompleted事件將處理程序編組到UI時,RunWorkerCompleted事件將無法運行因爲每個人都在等待另一個人,所以你會遇到一個死鎖 – Servy

+0

@Servy它取決於你在哪裏等待CountdownEvent。看起來我很清楚你應該從一個單獨的線程更新UI並因此等待完成我寫的不是確切的代碼,需要粘貼到一個項目中,而只是一個想法應該如何實施! –

+0

您的答案*特別是*提到在'FuncA'中等待,這顯然是在UI線程中運行的。創建一個全新的線程完全不合適,只能等待所有這些工作者完成。你根本不需要任何線程來做到這一點。 – Servy

0

當調用_bw.RunWorkerAsync()時,可以將該列表作爲參數傳遞。 RunWorkerAsync方法有一個重載方法,它將一種對象作爲參數。

您可以在bw_DoWork事件結束時從列表中刪除項目,而不是在bw_RunWorkerCompleted事件中。後者應該在更新某個UI元素時使用。

List<object> list = new List<object>(); 
FuncA() 
{ 
    foreach() 
    { 
     /* add an item to the list */ 
     _bw.RunWorkerAsync(list); 
    } 
    m_event.WaitOne(); /* Main thread waits here */ 
} 
static bw_DoWork() 
{ 
    // Do the stuff. 

    /* delete item from list */ 
    var list = e.Argument as List<object>; 
    /* if list is empty signal m_event.Set() */ 
} 
+0

然後在這個長時間運行的工作期間阻塞UI線程,從而凍結整個UI。不應該這樣做;當這項工作正在進行時,應該留下UI線程繼續處理消息。 – Servy