2008-12-11 77 views
10

我有一個場景,當我使用ThreadPool啓動3..10個線程時。每個線程完成它的工作並返回到ThreadPool。 當所有後臺線程完成後,可以在主線程中通知什麼選項?當所有後臺線程池線程完成時都會收到通知

目前,我正在使用本地方法,爲每個創建的線程增加一個變量,並在後臺線程即將完成時遞減該變量。 這工作得很好,但我很好奇,如果有更好的選擇。

回答

11

遞減變量(在線程之間)有點冒險,除非使用Interlocked.Decrement,但如果你有最後一個線程(即當它變爲零)引發一個事件時,這種方法應該沒問題。請注意,它必須位於「finally」塊中,以避免在異常情況下丟失它(再加上您不想終止進程)。

在「並行擴展」(或.NET 4.0)中,您也可以在這裏查看Parallel.ForEach選項......這可能是另一種將所有內容都作爲塊完成的方式。無需手動觀看。

+0

Hi Marc。 我正在以線程安全的方式執行此操作,使用Interlocked或lock關鍵字。它工作正常,並以非阻塞的方式。 我想知道是否有內置的基元來做到這一點。 謝謝。 – Valentin 2008-12-11 09:07:59

2

目前還沒有內建的方法來實現這一點 - 我發現它是使用池線程最大的難題之一。

正如Marc所說,這是在Parallel Extensions/.NET 4.0中正在修復的東西。

1

難道你不能給每個線程一個獨特的ManualResetEvent並且每個線程完成後都設置事件。然後,在主線程中,您可以等待傳入的所有事件。

0

如果您只是想知道所有工作何時完成,並且不需要比此更精細的信息,Marc的解決方案是最好的是你的情況)。

如果你想要一些線程來產生工作,並有一些其他線程來接收通知,你可以使用WaitHandle。代碼要長得多。

int length = 10; 
    ManualResetEvent[] waits = new ManualResetEvent[length]; 
    for (int i = 0; i < length; i++) { 
     waits[i] = new ManualResetEvent(false); 
     ThreadPool.QueueUserWorkItem((obj) => { 
      try { 

      } finally { 
       waits[i].Set(); 
      } 
     }); 
    } 

    for (int i = 0; i < length; i++) { 
     if (!waits[i].WaitOne()) 
      break; 
    } 

該方法的WaitOne,書面,始終返回true,但我已經寫這樣說,這讓你記住,有些重載需要超時作爲參數。

3

如果其不超過64個線程等待時,您可以使用WaitHandle.WaitAll的方法是這樣的:

List<WaitHandle> events = new List<WaitHandle>(); 
for (int i = 0; i < 64; i++) 
{ 
    ManualResetEvent mre = new ManualResetEvent(false); 
    ThreadPool.QueueUserWorkItem(
     delegate(object o) 
     { 
      Thread.Sleep(TimeSpan.FromMinutes(1)); 
      ((ManualResetEvent)o).Set(); 
     },mre); 
    events.Add(mre); 
} 
WaitHandle.WaitAll(events.ToArray()); 

執行將等到所有ManualResetEvents設置,或者,你可以使用了WaitAny方法。

WaitAny和WaitAll方法將阻止執行,但您可以簡單地使用列表或鏈接到產生的任務的ManualResetEvents字典,以便稍後確定線程是否完成。

+1

只要Main()不是單線程公寓([STAThread]),此方法就很有用。 – 2012-11-28 21:59:51

+0

爲什麼它需要「超過64個線程等待」? – jp2code 2015-02-18 21:59:16

0

怎麼樣使用信號量,並設置它的限制,就像你的線程池。有一個方法來獲取一個Semaphore,當你開始你的線程時被調用,當你的線程結束時釋放它,並且如果你已經佔用了所有的Semaphore,則會引發一個事件。

4

試試這個:https://bitbucket.org/nevdelap/poolguard

using (var poolGuard = new PoolGuard()) 
{ 
    for (int i = 0; i < ... 
    { 
     ThreadPool.QueueUserWorkItem(ChildThread, poolGuard); 
    } 
    // Do stuff. 
    poolGuard.WaitOne(); 
    // Do stuff that required the child threads to have ended. 

void ChildThread(object state) 
{ 
    var poolGuard = state as PoolGuard; 
    if (poolGuard.TryEnter()) 
    { 
     try 
     { 
      // Do stuff. 
     } 
     finally 
     { 
      poolGuard.Exit(); 
     } 
    } 
} 

多PoolGuards可以以不同的方式使用當線程已經結束跟蹤,以及時處理池已關閉尚未啓動的線程。嗨Marc。

相關問題