我有一個場景,我將不得不開始大量的線程(可能高達100),然後等待它們完成,然後執行任務(在另一個線程上)。等待所有線程完成的有用模式是什麼?
做這類工作的可接受模式是什麼?是簡單嗎?加入?還是現在有更高層次的抽象?
在VS2008中使用.NET 2.0。
我有一個場景,我將不得不開始大量的線程(可能高達100),然後等待它們完成,然後執行任務(在另一個線程上)。等待所有線程完成的有用模式是什麼?
做這類工作的可接受模式是什麼?是簡單嗎?加入?還是現在有更高層次的抽象?
在VS2008中使用.NET 2.0。
在.NET 3.5sp1或.NET 4中,TPL會使這更容易。但是,我將僅針對.NET 2功能進行定製。
有幾個選項。使用Thread.Join是完全可以接受的,特別是如果線程是您手動創建的所有線程。這非常簡單,可靠並且易於實施。這可能是我的選擇。
但是,另一種選擇是爲總工作量創建計數器,並在計數器達到零時使用重置事件。例如:
class MyClass {
int workToComplete; // Total number of elements
ManualResetEvent mre; // For waiting
void StartThreads()
{
this.workToComplete = 100;
mre = new ManualResetEvent(false);
int total = workToComplete;
for(int i=0;i<total;++i)
{
Thread thread = new Thread(new ThreadStart(this.ThreadFunction));
thread.Start(); // Kick off the thread
}
mre.WaitOne(); // Will block until all work is done
}
void ThreadFunction()
{
// Do your work
if (Interlocked.Decrement(ref this.workToComplete) == 0)
this.mre.Set(); // Allow the main thread to continue here...
}
}
應該向後運行線程啓動循環('for(int i = workToComplete; i> 0; --i)'),以便如果您的ThreadFunction在循環之前完成,Interlocked.Decrement並沒有與條件式一起使用。 – Tanzelax 2010-02-18 19:17:15
@坦澤拉斯:好點。我剛剛在那裏介紹了一個臨時處理(正確)的臨時工。很好的接收。 – 2010-02-18 19:29:44
你看過ThreadPool嗎?看起來像這裏 - ThreadPool tutorial,avtor解決了你所要求的相同任務。
waithandle.waitall有64個句柄的限制 – Jimmy 2010-02-18 18:34:15
您可以通過按順序等待每個元素上的WaitOne來做同樣的事情,但是 - 可以使用> 64個等待句柄。 (這就是說,我更喜歡我的方法......) – 2010-02-18 19:17:11
什麼是運作良好,對我來說是給每個線程的ManagedThreadId存儲在字典中,我啓動它,然後讓每個線程通過其ID後面通過回調方法,當它完成。回調方法從字典中刪除id並檢查字典的Count屬性;當它爲零時,你就完成了。確保鎖定詞典以添加和刪除詞典。
我不確定任何一種標準的線程鎖定或同步機制都能真正適用於這麼多的線程。但是,這可能是一些情況下,一些基本的消息傳遞可能是解決問題的理想解決方案。
與其使用Thread.Join會阻塞(並且可能很難管理這麼多的線程),您可能會嘗試設置另一個線程來聚合來自工作線程的完成消息。當聚合器收到所有預期的消息時,它就完成了。然後,您可以在聚合器和主應用程序線程之間使用一個WaitHandle來表示您的所有工作線程都已完成。
public class WorkerAggregator
{
public WorkerAggregator(WaitHandle completionEvent)
{
m_completionEvent = completionEvent;
m_workers = new Dictionary<int, Thread>();
}
private readonly WaitHandle m_completionEvent;
private readonly Dictionary<int, Thread> m_workers;
public void StartWorker(Action worker)
{
var thread = new Thread(d =>
{
worker();
notifyComplete(thread.ManagedThreadID);
}
);
lock (m_workers)
{
m_workers.Add(thread.ManagedThreadID, thread);
}
thread.Start();
}
private void notifyComplete(int threadID)
{
bool done = false;
lock (m_workers)
{
m_workers.Remove(threadID);
done = m_workers.Count == 0;
}
if (done) m_completionEvent.Set();
}
}
請注意,我沒有測試過上面的代碼,所以它可能不是100%正確的。不過,我希望它能說明這個概念足夠有用。
您需要啓動線程(將其添加到m_workers之後)......它將永遠不會像寫入的那樣運行。 – 2010-02-18 19:30:47
哎呀,我的壞。固定。 – jrista 2010-02-19 21:50:55
你可以使用並行擴展而不是創建自己的線程嗎? – Jimmy 2010-02-18 18:28:45