我有一個場景,當我使用ThreadPool啓動3..10個線程時。每個線程完成它的工作並返回到ThreadPool。 當所有後臺線程完成後,可以在主線程中通知什麼選項?當所有後臺線程池線程完成時都會收到通知
目前,我正在使用本地方法,爲每個創建的線程增加一個變量,並在後臺線程即將完成時遞減該變量。 這工作得很好,但我很好奇,如果有更好的選擇。
我有一個場景,當我使用ThreadPool啓動3..10個線程時。每個線程完成它的工作並返回到ThreadPool。 當所有後臺線程完成後,可以在主線程中通知什麼選項?當所有後臺線程池線程完成時都會收到通知
目前,我正在使用本地方法,爲每個創建的線程增加一個變量,並在後臺線程即將完成時遞減該變量。 這工作得很好,但我很好奇,如果有更好的選擇。
遞減變量(在線程之間)有點冒險,除非使用Interlocked.Decrement
,但如果你有最後一個線程(即當它變爲零)引發一個事件時,這種方法應該沒問題。請注意,它必須位於「finally」塊中,以避免在異常情況下丟失它(再加上您不想終止進程)。
在「並行擴展」(或.NET 4.0)中,您也可以在這裏查看Parallel.ForEach
選項......這可能是另一種將所有內容都作爲塊完成的方式。無需手動觀看。
目前還沒有內建的方法來實現這一點 - 我發現它是使用池線程最大的難題之一。
正如Marc所說,這是在Parallel Extensions/.NET 4.0中正在修復的東西。
難道你不能給每個線程一個獨特的ManualResetEvent並且每個線程完成後都設置事件。然後,在主線程中,您可以等待傳入的所有事件。
如果您只是想知道所有工作何時完成,並且不需要比此更精細的信息,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,但我已經寫這樣說,這讓你記住,有些重載需要超時作爲參數。
如果其不超過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字典,以便稍後確定線程是否完成。
只要Main()不是單線程公寓([STAThread]),此方法就很有用。 – 2012-11-28 21:59:51
爲什麼它需要「超過64個線程等待」? – jp2code 2015-02-18 21:59:16
怎麼樣使用信號量,並設置它的限制,就像你的線程池。有一個方法來獲取一個Semaphore,當你開始你的線程時被調用,當你的線程結束時釋放它,並且如果你已經佔用了所有的Semaphore,則會引發一個事件。
試試這個: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。
Hi Marc。 我正在以線程安全的方式執行此操作,使用Interlocked或lock關鍵字。它工作正常,並以非阻塞的方式。 我想知道是否有內置的基元來做到這一點。 謝謝。 – Valentin 2008-12-11 09:07:59