本文主要描述在. netframework 中 (实验环境. netframework 版本为 4.6.1) 提供两种方式等待多个子线程执行完毕.
ManualResetEvent
在多线程中, 将 ManualResetEvent 实例作为方法传入, 线程执行完毕后可以设置标志位来标识当前线程已经执行完毕. 代码如下:
- List<ManualResetEvent> manualResetEvents = new List<ManualResetEvent>();
- /// <summary>
- /// ManualResetEvent 标志多线程是否执行完毕
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="e"></param>
- private void btn_ManualResetEvent_Click(object sender, EventArgs e)
- {
- // SetBtnEnabled(false);
- Stopwatch watch = new Stopwatch();
- watch.Start();
- for (int i = 0; i <threadCount; i++)
- {
- ManualResetEvent manualReset = new ManualResetEvent(false);
- manualResetEvents.Add(manualReset);
- ThreadPool.QueueUserWorkItem(ManualResetEventMethod, manualReset);
- }
- // 等待所有线程执行完毕
- WaitHandle.WaitAll(manualResetEvents.ToArray());
- // 暂停 watch, 获取多线程执行时间
- watch.Stop();
- long time = watch.ElapsedMilliseconds;
- lab_time.Text = time.ToString();
- // SetBtnEnabled(true);
- // 释放句柄
- manualResetEvents.Clear();
- }
- private void ManualResetEventMethod(object obj)
- {
- Thread.Sleep(1000);
- ManualResetEvent mre = (ManualResetEvent)obj;
- mre.Set();
- }
- 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)通知主线程, 直到所有子线程执行完成, 主线程再继续执行, 代码如下:
- object locker = new object();
- int threadCount = 1000;
- int finshCount = 0;
- /// <summary>
- /// Monitor 线程之间同步标记多线程执行完毕
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="e"></param>
- private void btn_Monitor_Click(object sender, EventArgs e)
- {
- finshCount = 0;
- SetBtnEnabled(false);
- Stopwatch watch = new Stopwatch();
- watch.Start();
- for (int i = 0; i < threadCount; i++)
- {
- Thread trd = new Thread(new ParameterizedThreadStart(MonitorMethod));
- trd.Start(i);
- }
- lock (locker)
- {
- while (finshCount != threadCount)
- {
- Monitor.Wait(locker);// 等待
- }
- }
- // 所有线程执行完毕, 获取执行时间
- watch.Stop();
- long time = watch.ElapsedMilliseconds;
- lab_time.Text = time.ToString();
- SetBtnEnabled(true);
- }
- private void MonitorMethod(object obj)
- {
- Thread.Sleep(1000);
- lock (locker)
- {
- finshCount++;
- Monitor.Pulse(locker); // 完成, 通知等待队列, 告知已完, 执行下一个.
- }
- }
- 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 语言的协程效率最高
来源: https://www.cnblogs.com/ziyunxiu/p/10928331.html