假设现在有几个不同的任务需要排列进入线程池中执行,我如要在任务全部完成时通知这一批的客户,告诉他们交付的任务都已经完成了。但是如何才能知道线程有没有执行完成呢?
ThreadPool 提供的是静态方法,没有执行完成的 CallBack 之类的方法,但是 RegisterWaitForSingleObject 为我们提供了信号量,然后可以通过 System.Threading.WaitHandle.WaitAll 或者 WaitAny 去阻塞主线程,等待信号量的通知。
先创建一个信号量,命名的信号量可以在环境中进行查询获取
EventWaitHandle ewHandle = new EventWaitHandle(false, EventResetMode.ManualReset, "EventWaitHandle" + i.ToString());
然后将信号量和执行任务的方法一起传递进入线程池中
// 创建委托调用的方法 WaitOrTimerCallback wtCallback = new WaitOrTimerCallback(CalculateCallBack); // 加入线程池 cell 对象为自定义的执行单元对象,包含了 本次任务的相关数据和关联的信号量 ThreadPool.RegisterWaitForSingleObject(ewHandle, wtCallback, cell, 0, true);
在加入线程池后,主进程可以使用 Wait 方式等待线程池的执行
// 等待全部线程的完成通知System.Threading.WaitHandle.WaitAll(listHandle, Timeout.Infinite);
在任务执行完成后使用信号量发出通知,告诉主进程中的等待可以结束
// 信号量的完成通知 cellState.WaitHandle.Set();
下面是一组测试图,使用线程池测试浮点数运算效率 【一万次随机的浮点数运算】
使用 4 个线程运算,使用了 80 秒, CPU 使用状态 保持在 60 % 上
在单线程的计算时 ,使用了 70 秒,CPU 使用状态 保持在 40 % 上
所以,线程不是越多越来,对于单一内容的任务多线程反而会因线程调度造成巨大的效率和资源浪费。好吧,其实这个不是本文的重点,重点是MARK 一下在使用线程池时,如何利用信号量通知进程执行情况。
MARK 通过信号量可以知道线程的完成情况,最后直接简单贴上测试的代码
using System;using System.Threading;namespace ConsoleApplication1{ class Program { static void Main(string[] args) { // 性能计算器 System.Diagnostics.Stopwatch sdsWatch = new System.Diagnostics.Stopwatch(); Random randomSeed = new Random(); sdsWatch.Start(); Int32 sumCount = 100000; double threadCount = 1; bool usedThread = false; Console.WriteLine("请输入计算量:"); sumCount = Convert.ToInt32(Console.ReadLine().Trim()); Console.WriteLine("是否使用多线程测试?Y/N"); usedThread = Console.ReadLine().Trim().ToUpper() == "Y"; if (usedThread) { Console.WriteLine("请输入拟定的线程数:"); threadCount = Convert.ToInt32(Console.ReadLine().Trim()); } Console.WriteLine("计算任务:" + sumCount + " ,线程数:" + threadCount); Console.ReadLine(); // 保存信号量的列表用于后续的访问 EventWaitHandle[] listHandle = new EventWaitHandle[(int)threadCount]; for (int i = 0; i < threadCount; i++) { EventWaitHandle ewHandle = new EventWaitHandle(false, EventResetMode.ManualReset, "EventWaitHandle" + i.ToString()); listHandle[i] = ewHandle; CalCell cell = new CalCell() { Start = (int)(i / threadCount * sumCount), End = (int)((i + 1) / threadCount * sumCount), WaitHandle = ewHandle }; //WaitCallback wCallback = new WaitCallback(CalculateCallBack); //ThreadPool.QueueUserWorkItem(wCallback, cell); WaitOrTimerCallback wtCallback = new WaitOrTimerCallback(CalculateCallBack); ThreadPool.RegisterWaitForSingleObject(ewHandle, wtCallback, cell, 0, true); } // 等待全部线程的完成通知 System.Threading.WaitHandle.WaitAll(listHandle, Timeout.Infinite); //此处也可以使用 WaitAny 和 SignalAndWait 可以考虑实现逐次通知 【个人感觉是有时间风险的】 //EventWaitHandle callBackCompleted = //new EventWaitHandle(false, EventResetMode.ManualReset, "CompletedWaitHandle" + i.ToString()); //System.Threading.WaitHandle.SignalAndWait(callBackCompleted, ewHandle, Timeout.Infinite, true); sdsWatch.Stop(); Console.WriteLine(string.Format("====== Calculate Tasks:{0,10}, Threads:{1,10} ===========", sumCount, threadCount)); Console.WriteLine(string.Format("====== Milliseconds:{0,10}, Ticks:{1,10} ===========", sdsWatch.ElapsedMilliseconds, sdsWatch.ElapsedTicks)); Console.ReadLine(); } ////// 计算 /// /// private static void CalculateCallBack(object state) { CalCell cellState = (CalCell)state; double value = 0.0D; Random randomSeed = new Random(); for (int i = cellState.Start; i < cellState.End; i++) { value = i * randomSeed.NextDouble(); Console.WriteLine(string.Format("{0,-15:F6}Thread ID :{1}", value, Thread.CurrentThread.ManagedThreadId)); } } ////// 计算 /// /// private static void CalculateCallBack(object state, bool timeOut) { CalCell cellState = (CalCell)state; double value = 0.0D; Random randomSeed = new Random(); for (int i = cellState.Start; i < cellState.End; i++) { value = i * randomSeed.NextDouble(); Console.WriteLine(string.Format("{0,-15:F6}Thread ID :{1}", value, Thread.CurrentThread.ManagedThreadId)); } // 信号量的完成通知 cellState.WaitHandle.Set(); } ////// 计算单元 /// public class CalCell { public int Start; public int End; public EventWaitHandle WaitHandle; } }}