1. 程式人生 > >netframework中等待多個子執行緒執行完畢並計算執行時間

netframework中等待多個子執行緒執行完畢並計算執行時間

本文主要描述在.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         }
View Code

      注意:

     在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         }
View Code

       在一次開啟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