netframework中等待多個子執行緒執行完畢並計算執行時間
阿新 • • 發佈:2019-05-27
本文主要描述在.netframework中(實驗環境.netframework版本為4.6.1)提供兩種方式等待多個子執行緒執行完畢。
- ManualResetEvent
在多執行緒中,將ManualResetEvent例項作為方法傳入,執行緒執行完畢後可以設定標誌位來標識當前執行緒已經執行完畢。程式碼如下:
1 List<ManualResetEvent> manualResetEvents = new List<ManualResetEvent>(); 2 /// <summary> 3 /// ManualResetEvent標誌多執行緒是否執行完畢 4 /// </summary> 5 /// <param name="sender"></param> 6 /// <param name="e"></param> 7 private void btn_ManualResetEvent_Click(object sender, EventArgs e) 8 { 9 // SetBtnEnabled(false); 10 Stopwatch watch = new Stopwatch(); 11 watch.Start(); 12 for (int i = 0; i < threadCount; i++) 13 { 14 ManualResetEvent manualReset = new ManualResetEvent(false); 15 manualResetEvents.Add(manualReset); 16 ThreadPool.QueueUserWorkItem(ManualResetEventMethod, manualReset); 17 } 18 //等待所有執行緒執行完畢 19 WaitHandle.WaitAll(manualResetEvents.ToArray()); 20 //暫停watch,獲取多執行緒執行時間 21 watch.Stop(); 22 long time = watch.ElapsedMilliseconds; 23 lab_time.Text = time.ToString(); 24 25 // SetBtnEnabled(true); 26 27 //釋放控制代碼 28 manualResetEvents.Clear(); 29 } 30 31 private void ManualResetEventMethod(object obj) 32 { 33 Thread.Sleep(1000); 34 ManualResetEvent mre = (ManualResetEvent)obj; 35 mre.Set(); 36 }
注意:
在WaitHandle.WaitAll方法中,等待的控制代碼不能超過64,所以每次用完後,需要手動呼叫Clear方法進行釋放。
如果等待的執行緒超過64個,可以參考部落格:https://www.cnblogs.com/xiaofengfeng/archive/2012/12/27/2836183.html,在該部落格中,通過對ManualResetEvent的封裝,能夠使等待的控制代碼超過64(測試環境下一次起1000個執行緒,沒有問題)
- Monitor
在主執行緒中通過Monitor.Wait(locker)達到阻塞的目的,子執行緒執行完畢通過 Monitor.Pulse(locker)通知主執行緒,直到所有子執行緒執行完成,主執行緒再繼續執行,程式碼如下:
1 object locker = new object(); 2 int threadCount = 1000; 3 int finshCount = 0; 4 /// <summary> 5 /// Monitor執行緒之間同步標記多執行緒執行完畢 6 /// </summary> 7 /// <param name="sender"></param> 8 /// <param name="e"></param> 9 private void btn_Monitor_Click(object sender, EventArgs e) 10 { 11 finshCount = 0; 12 SetBtnEnabled(false); 13 Stopwatch watch = new Stopwatch(); 14 watch.Start(); 15 for (int i = 0; i < threadCount; i++) 16 { 17 Thread trd = new Thread(new ParameterizedThreadStart(MonitorMethod)); 18 trd.Start(i); 19 } 20 lock (locker) 21 { 22 while (finshCount != threadCount) 23 { 24 Monitor.Wait(locker);//等待 25 } 26 } 27 //所有執行緒執行完畢,獲取執行時間 28 watch.Stop(); 29 long time = watch.ElapsedMilliseconds; 30 lab_time.Text = time.ToString(); 31 32 SetBtnEnabled(true); 33 } 34 35 private void MonitorMethod(object obj) 36 { 37 Thread.Sleep(1000); 38 lock (locker) 39 { 40 finshCount++; 41 Monitor.Pulse(locker); //完成,通知等待佇列,告知已完,執行下一個。 42 } 43 }
在一次開啟10、1000個執行緒兩種環境下,分別測試以上兩種方式,ManualResetEvent在多次執行時,前幾次耗時會比較大,後續耗時會減少並且穩定下來,接近 Monitor的速度。相對而言,Monitor的效率更高。
如果瞭解過go語言,會發現通過sync包下的WaitGroup也可以達到同樣的目的,程式碼如下:
package main import ( "fmt" "sync" "time" ) var wg sync.WaitGroup var count = 1000 func main() { startTime := time.Now().Unix() wg.Add(count) for i := 0; i < count; i++ { go func() { defer wg.Done() time.Sleep(time.Second) }() } fmt.Println("waiting for all goroutine") wg.Wait() endTime := time.Now().Unix() fmt.Printf("all goroutine is done! time:%v s", (endTime-startTime)) }
相較而言,go語言的協程效率最高
&n