我有一個主線程的應用程序和N工作線程。在某些時候,我需要主線程等待,直到所有的線程完成其工作的一部分。如何停止一個線程,直到n個線程完成其工作
我通常會使用Monitor.Wait()和Monitor.Pulse(),但這會阻止線程同時工作。
任何想法如何做到這一點?
在此先感謝。
我有一個主線程的應用程序和N工作線程。在某些時候,我需要主線程等待,直到所有的線程完成其工作的一部分。如何停止一個線程,直到n個線程完成其工作
我通常會使用Monitor.Wait()和Monitor.Pulse(),但這會阻止線程同時工作。
任何想法如何做到這一點?
在此先感謝。
好吧,我現在做(使用你的想法),並似乎工作是這樣的:
我宣佈ManualResetEvent的列表:
Private m_waitHandles As List(Of Threading.ManualResetEvent)
該進程接受傳入的Tcp連接並在每個連接上啓動一個線程。因此,在新的客戶端處理程序我已經添加以下代碼:
Dim waitHandle As Threading.ManualResetEvent
waitHandle = New Threading.ManualResetEvent(True)
SyncLock (DirectCast(m_waitHandles, IList).SyncRoot)
m_waitHandles.Add(waitHandle)
End SyncLock
''# Do all the work
StoppableMethod()
SyncLock (DirectCast(m_waitHandles, IList).SyncRoot)
waitHandle = m_waitHandles.Item(Threading.WaitHandle.WaitAny(m_waitHandles.ToArray()))
End SyncLock
waitHandle.Reset()
NonStoppableMethod()
waitHandle.Set()
SyncLock (DirectCast(m_waitHandles, IList).SyncRoot)
m_waitHandles.Remove(waitHandle)
End SyncLock
做的最後一件事是修改Stop方法,以確保停止操作不會與NonStoppableMethod內的任何線程中完成的:
SyncLock (DirectCast(m_waitHandles, IList).SyncRoot)
If m_waitHandles.Count > 0 Then
Threading.WaitHandle.WaitAll(m_waitHandles.ToArray())
End If
End SyncLock
我不確定這是否以正確的方式完成,因爲這是我第一次處理這樣的事情。你覺得這是好的,是一個好方法嗎?
感謝所有,配偶!
很高興我們可以幫助! – 2009-12-30 17:37:51
這就是所謂的一個障礙:http://programmingexamples.wikidot.com/java-barrier
嗷,但如果你只需要第一個線程等待其餘通過某些時候,你想讓對方依然繼續工作,然後用大小爲N的信號讓所有其他線程需要它,而第一個線程等待後,他們獲得它..
如果你只需要等待,直到線程終止,怎麼樣Thread.Join
?在.NET 4.0中,您可以使用Task.WaitAll
。如果你需要等到他們完成任務的一部分,這是一個小竅門。在當前版本的.NET中,請看WaitHandle.WaitAll
/Threading.ManualResetEvent
。在.NET 4.0中,您可以使用Threading.Barrier
。
不,線程不會終止,只是在他們的代碼中重複一點。 – 2009-12-30 16:21:39
@SoMoS:查看MSDN上有關'WaitHandle.WaitAll'和'ManualResetEvent'的示例。 – jason 2009-12-30 17:10:15
做一些類似垃圾收集。您將編寫一個ThreadManager,其中有多少個線程正在運行。當主線程啓動一個新工作者時,ThreadManager將增加工作者的數量。當工作完成時,它會通知ThreadManager誰將遞減其線程數。當它有零個工作線程時,ThreadManager將喚醒主線程。
修改或讀取時不要忘記鎖定計數變量。 – 2009-12-30 16:51:13
.NET 4.0將包括System.Threading.Barrier
類,它將使多個線程之間的同步更容易。有一些很好的示例代碼的博客帖子可以找到here。
Similar functionality can be achieved在.NET 3.0+中使用多個WaitHandles,如在MSDN上的this example中所示。
MSDN的例子的小結:
const int numberOfWorkers = 5;
static void Main()
{
var handles = new ManualResetEvent[numberOfWorkers];
for (int i = 0; i < numberOfWorkers; i++)
{
handles[i] = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem(o => worker.Work(), null);
}
// Wait for all workers to finish before continuing
WaitHandle.WaitAll(handles);
/* continue execution... */
}
看起來像我需要的,但我現在需要在3.5。有一種方法可以做到這一點? – 2009-12-30 16:22:25
@SoMoS,如果我明白你要做什麼,你可以用'AutoResetEvents'(我在我的答案中描述)使用'WaitAll'來做你想要的。 – 2009-12-30 17:00:24
@Jeff Sternal:我相信我們都提出了相同的解決方案,但有不同的實現。 – 2009-12-30 17:03:12
由於在一些實現,有多少把手WaitHandle.WaitAll()
可以....處理,(見msdn-WaitHandle.WaitAll()的限制,我創建了一個實用方法爲此:
public static void WaitAll(WaitHandle[] handles)
{
if (handles == null)
throw new ArgumentNullException("handles",
"WaitHandle[] handles was null");
foreach (WaitHandle wh in handles) wh.WaitOne();
}
用法是添加等待句柄每個線程的陣列,然後調用上述工具方法(傳遞數組)的所有線程已經啓動之後
。List<WaitHandle> waitHndls = new List<WaitHandle>();
foreach (MyType mTyp in MyTypeCollection)
{
ManualResetEvent txEvnt = new ManualResetEvent(false);
int qryNo1 = ++qryNo;
ThreadPool.QueueUserWorkItem(
delegate
{
try
{
// Code to execute whatever thread's function is...
}
catch (SomeCustomException iX)
{
// catch code
} }
finally { lock (locker) txEvnt.Set(); }
});
waitHndls.Add(txEvnt);
}
util.WaitAll(waitHndls.ToArray());
爲什麼不使用'WaitHandle.WaitAll'? – 2009-12-30 16:35:14
因爲WaitAll有多少個句柄是有限制的......沒有雙關語意思)...句柄...從msdn中,「WaitAll方法在所有句柄被髮信號時返回,在一些實現中,如果超過64句柄被傳遞時,拋出NotSupportedException異常。「 – 2009-12-30 16:46:39
確實如此,但'foreach'方法不適用於'AutoResetEvents'(更確切地說,它會產生不可預知的結果,並且可能導致死鎖)。 – 2009-12-30 17:12:15
好像WaitHandle.WaitAll
應該解決這個問題。
您的主線程將需要保持對工作線程等待句柄的引用。當它需要同步時,將這些句柄傳遞給上述方法。工作線程在其代碼中的適當位置線程發出信號。
如果工作線程循環或需要「脈衝」多次,你可以使用AutoResetEvents
,像這樣:
public void WorkerMethod() {
DoFirstThing();
this.autoResetEvent.Set();
DoSecondThing();
this.autoResetEvent.Set();
// etc.
}
如果沒有(如果只需要在主線程知道工作者線程已經過去了一些門檻),ManualResetEvents
會很好。
有幾件事情需要提防使用了WaitAll時(從MSDN WaitAll
文檔)的:
在一些實施中,如果超過64 句柄傳遞,一個 NotSupportedException異常被拋出。如果 該陣列包含重複項,則 調用失敗,並返回 DuplicateWaitObjectException。
但是,一個進程真的可以利用超過64個線程的優勢很少,所以這個限制通常並不重要。
我喜歡你的方法。我必須考慮到這是爲了處理傳入的tcp客戶端,所以也許我有更多的64個線程(每個客戶端一個)。我如何檢查這個限制適用於哪裏? – 2009-12-30 17:17:17
不幸的是,這些信息很難得到。我可以在Reflector中看到它被硬編碼到了.NET 2.0實現中,但是手頭沒有3.5程序集。這裏有一篇文章提出了一種巧妙的解決方案來克服極限,這與David Souther在另一個答案中提出的類似:創建自己的'WaitHandle'類來實現引用計數,以便在所有工作線程中共享一個'WaitHandle' 。 http://msdn.microsoft.com/en-us/magazine/cc163914.aspx。 – 2009-12-30 22:07:11
使用Thread.Join(即阻塞調用線程,直到某個線程終止,同時繼續執行標準的COM和SendMessage消息泵)像實例方法:
using System;
using System.Threading;
class IsThreadPool
{
static void Main()
{
AutoResetEvent autoEvent = new AutoResetEvent(false);
Thread regularThread =
new Thread(new ThreadStart(ThreadMethod));
regularThread.Start();
ThreadPool.QueueUserWorkItem(new WaitCallback(WorkMethod),
autoEvent);
// __________ Wait for foreground thread to end. __________
regularThread.Join();
// Wait for background thread to end.
autoEvent.WaitOne();
}
static void ThreadMethod()
{
Console.WriteLine("ThreadOne, executing ThreadMethod, " +
"is {0}from the thread pool.",
Thread.CurrentThread.IsThreadPoolThread ? "" : "not ");
}
static void WorkMethod(object stateInfo)
{
Console.WriteLine("ThreadTwo, executing WorkMethod, " +
"is {0}from the thread pool.",
Thread.CurrentThread.IsThreadPoolThread ? "" : "not ");
// Signal that this thread is finished.
((AutoResetEvent)stateInfo).Set();
}
}
我不等待其他線程終止,只是爲了超過某個點。 – 2009-12-30 16:45:07
@SoMoS:我明白。爲什麼不把它分成兩個線程呢? :)等待第一個,然後離開第二個。 – serhio 2009-12-30 16:59:09
在兩個線程中分割工作看起來不太好,第一個在第二個線程結束時開始第二個。如果我需要檢查2點而不是1點怎麼辦?無論如何謝謝隊友! – 2009-12-30 17:05:19
嘗試使用這樣的:
int threadsCompleted = 0;
int numberOfThreads = 4;
ManualResetEvent completedEvent = new ManualResetEvent(false);
在每個線程:
// Do task
if (Interlocked.Increment(threadsCompleted) == numberOfThreads)
completedEvent.Set();
主線程:
completedEvent.WaitOne();
所有在互聯網上的人嘗試使用EventHandles
和WaitAll()
數組。我想出了以下的課程,這個課程在資源上要輕得多。我試圖想到不同的比賽場景,我相信這段代碼沒有競爭條件。(在遞減和檢查Count
上的條件之間存在理論上的競爭,但據我所知它不影響功能,並且代碼將始終工作。)
要使用此類,需要同步的所有線程必須調用其方法Wait()
。他們將阻止,直到Count
線程數稱爲Wait()
。一個實例只能用於同步一次(它不能被重置)。
internal class ThreadBarrier
{
private ManualResetEvent BarrierEvent;
private int Count;
internal ThreadBarrier(int count)
{
BarrierEvent = new ManualResetEvent(false);
Count = count;
}
internal void Wait()
{
Interlocked.Decrement(ref Count);
if (Count > 0)
BarrierEvent.WaitOne();
else
BarrierEvent.Set();
}
}
你是我想出的方法,直到類名。我的不同之處在於我有一個單獨的釋放活動線程的方法,以更好地控制哪些線程塊。 – 2012-12-20 17:17:49
只是一個想法:你在下面的評論中描述,你想要表示某個部分已經通過,因爲線程沒有終止。這表示每個線程中有一個循環。您的行爲是否定義爲工作線程在循環中的第二次迭代,因爲它們已經表明它們已完成? – 2009-12-30 16:54:00
@San:不,它不是一個循環,它只是一個很長的操作,爲了避免一些異常,在某個點之前無法停止,所以我需要鎖定Stop方法,直到所有線程都通過該點。 – 2009-12-30 17:02:51