多线程永远都是一个热门的话题,因为直接关系到程序的性能提升,也是开发必须要懂得的知识点,其实在网上随便一搜都会有海量的文章说这个话题,所以这里对于基础知识废话不多说,只结合多年实践做一个总结。
========== 原创作品 作者:Yokeqi 出处:博客园 ==========
一、知识点概括
二、具体实例演示如何实现
> Thread
//ThreadPool.GetMaxThreads(out int maxTCount, out int maxPCount); //ThreadPool.GetMinThreads(out int minTCount, out int minPCount); //ThreadPool.SetMaxThreads(maxTCount, maxPCount);// 调整最大线程数 //ThreadPool.SetMinThreads(minTCount, minPCount);// 调整最小线程数 long tick = C_ITEM_COUNT; ManualResetEvent signal = new ManualResetEvent(false); Console.WriteLine("========== 示例:采用Thread执行处理 =========="); for (int i = 0; i < C_ITEM_COUNT; i++) { new Thread((obj) => { Thread.Sleep(500); Console.Write(" {0} ", obj); if (Interlocked.Decrement(ref tick) == 0) signal.Set(); }).Start(i); } Console.Write(" 等待子线程执行 "); signal.WaitOne(); Console.WriteLine(); Console.WriteLine("全部线程执行完毕,按任意键继续...");
> ThreadPool
tick = C_ITEM_COUNT; signal.Reset(); Console.WriteLine(); Console.WriteLine("========== 示例:采用ThreadPool执行处理 =========="); for (int i = 0; i < C_ITEM_COUNT; i++) { ThreadPool.QueueUserWorkItem((obj) => { Thread.Sleep(500); Console.Write(" {0} ", obj); if (Interlocked.Decrement(ref tick) == 0) signal.Set(); }, i); } Console.Write(" 等待子线程执行 "); signal.WaitOne(); Console.WriteLine(); Console.WriteLine("全部线程执行完毕,按任意键继续...");
> Task
tick = C_ITEM_COUNT; signal.Reset(); Console.WriteLine(); Console.WriteLine("========== 示例:采用Task执行处理,注意取消了处理{0}的进程 ==========", C_ITEM_COUNT - 2); var tasks = new Tuple<Task, CancellationTokenSource>[C_ITEM_COUNT]; for (int i = 0; i < C_ITEM_COUNT; i++) { var cts = new CancellationTokenSource(); var task = Task.Factory.StartNew((obj) => { Thread.Sleep(500); Console.Write(" {0} ", obj); }, i, cts.Token); task.ContinueWith((t) => { if (Interlocked.Decrement(ref tick) == 0) signal.Set(); }); tasks[i] = new Tuple<Task, CancellationTokenSource>(task, cts); } tasks[C_ITEM_COUNT - 2].Item2.Cancel();// 取消线程。 Console.Write(" 等待子线程执行 "); signal.WaitOne(); Console.WriteLine(); Console.WriteLine("全部线程执行完毕,按任意键继续...");
> Parallel
tick = C_ITEM_COUNT; signal.Reset(); Console.WriteLine(); Console.WriteLine("========== 示例:采用Parallel执行处理 =========="); Parallel.For(0, C_ITEM_COUNT, obj => { Thread.Sleep(500); Console.Write(" {0} ", obj); if (Interlocked.Decrement(ref tick) == 0) signal.Set(); }); Console.Write(" 等待子线程执行 "); signal.WaitOne(); Console.WriteLine(); Console.WriteLine("全部线程执行完毕,按任意键继续...");
执行结果如下
三、性能对比
> 循环数:200,线程池参数:默认
> 循环数:200,线程池参数:50 - 1000
> 循环数200,线程池参数:100-1000
> 循环数200,线程池参数:200-1000
最大线程数 ~ 最小线程数 | Thread(ms) | ThreadPool(ms) | Task(ms) | Parallel(ms) |
2047/1000 ~ 12/12 | 2712.09 | 8057.14 | 8585.01 | 7526.57 |
1000/1000 ~ 50/50 | 2733.25 | 2289.96 | 2218.29 | 3660.33 |
1000/1000 ~ 100/100 | 2503.08 | 1620.73 | 1534.50 | 1742.78 |
1000/1000 ~ 200/200 | 2999.27 | 1436.24 | 1150.21 | 935.22 |
四、结论
> Thread就像脱缰的野马,不受控制,创建多少就运行多少,可能少量时效率是高了,量大的时候除了性能没优势,还可能导致句柄泄露。
> ThreadPool与Task类似,但Task相比效率更高用法更灵活。
> Parallel自带了同步功能,不需要用信号量来做额外的同步等待。
> ThreadPool、Task、Parallel的性能都取决于线程池最大线程数和最小线程数。
> 推荐使用 Task 和 Parallel,具体用哪个可以参考用法自己斟酌。