关于“分叉/联接方案”的一般做法
2013-03-30 11:12
369 查看
分叉/联接方案是指:在需要多线程计算的场合,通过在步骤A创建N个执行线程(分叉)后等待所有的线程执行完毕在执行步骤B(联接)。
.NET2.0
在.NET2.0的时代,我们通常会使用 ThreadPool.QueueUserWorkItem 创建N个执行线程,通过为每个线程绑定一个ManualResetEvent 对象,再通过WaitHandle.WaitAll方法执行等待;不过这里有个问题,就是WaitAll方法只能等待一定数量的线程,通常为64,一旦我们创建的线程超过64,会抛出如下的异常:
具体的代码说明,请参考 C#多线程之三:解决多线程编程中大并发数等待唤醒的问题
在上面的《C#多线程之三:解决多线程编程中大并发数等待唤醒的问题》文章中,作者通过创建了一个MutipleThreadResetEvent类,通过Interlocked.Decrement方法进行计数来实现。在这里我Copy了他的代码如下:
测试代码如下:
.NET4.0
在.NET Framework4.0中,微软为我们提供了CountdownEvent类。
“System.Threading.CountdownEvent 是一个同步基元,它在收到一定次数的信号之后,将会解除对其等待线程的锁定。 CountdownEvent 专门用于以下情况:您必须使用ManualResetEvent 或 ManualResetEventSlim,并且必须在用信号通知事件之前手动递减一个变量。 例如,在分叉/联接方案中,您可以只创建一个信号计数为 5 的CountdownEvent,然后在线程池上启动五个工作项,并且让每个工作项在完成时调用 Signal。 每次调用 Signal 时,信号计数都会递减 1。 在主线程上,对 Wait 的调用将会阻塞,直至信号计数为零。”
下面是微软给出的测试代码:
更多的其它使用方式,请参阅 CountdownEvent。
使用 System.Threading.Tasks.Task类。代码如下:
运行时截图如下:
.NET2.0
在.NET2.0的时代,我们通常会使用 ThreadPool.QueueUserWorkItem 创建N个执行线程,通过为每个线程绑定一个ManualResetEvent 对象,再通过WaitHandle.WaitAll方法执行等待;不过这里有个问题,就是WaitAll方法只能等待一定数量的线程,通常为64,一旦我们创建的线程超过64,会抛出如下的异常:
WaitHandles must be less than or equal to 64
具体的代码说明,请参考 C#多线程之三:解决多线程编程中大并发数等待唤醒的问题
在上面的《C#多线程之三:解决多线程编程中大并发数等待唤醒的问题》文章中,作者通过创建了一个MutipleThreadResetEvent类,通过Interlocked.Decrement方法进行计数来实现。在这里我Copy了他的代码如下:
/******************************************************************************** * Copyright © 2001 - 2010Comit. All Rights Reserved. * 文件:MutipleThreadResetEvent.cs * 作者:杨柳 * 日期:2010年11月13日 * 描述:封装 ManualResetEvent ,该类允许一次等待N(N>64)个事件执行完毕 * * 解决问题:WaitHandle.WaitAll(evetlist)方法最大只能等待64个ManualResetEvent事件 * *********************************************************************************/ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace TestMutipleThreadRestEvent { /// <summary> /// 封装ManualResetEvent /// </summary> public class MutipleThreadResetEvent : IDisposable { private readonly ManualResetEvent done; private readonly int total; private long current; /// <summary> /// 构造函数 /// </summary> /// <param name="total">需要等待执行的线程总数</param> public MutipleThreadResetEvent(int total) { this.total = total; current = total; done = new ManualResetEvent(false); } /// <summary> /// 唤醒一个等待的线程 /// </summary> public void SetOne() { // Interlocked 原子操作类 ,此处将计数器减1 if (Interlocked.Decrement(ref current) == 0) { //当所以等待线程执行完毕时,唤醒等待的线程 done.Set(); } } /// <summary> /// 等待所以线程执行完毕 /// </summary> public void WaitAll() { done.WaitOne(); } /// <summary> /// 释放对象占用的空间 /// </summary> public void Dispose() { ((IDisposable)done).Dispose(); } } }
测试代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace TestMutipleThreadRestEvent { /// <summary> /// 测试MutipleThreadResetEvent /// </summary> class Program { static int i = 0; /// <summary> /// 主方法 /// </summary> /// <param name="args">参数</param> static void Main(string[] args) { //假设有100个请求线程 int num = 100; //使用 MutipleThreadResetEvent using (var countdown = new MutipleThreadResetEvent(num)) { for (int i=0;i<num;i++) { //开启N个线程,传递MutipleThreadResetEvent对象给子线程 ThreadPool.QueueUserWorkItem(MyHttpRequest, countdown); } //等待所有线程执行完毕 countdown.WaitAll(); } Console.WriteLine("所有的网络请求以及完毕,可以继续下面的分析..."); Console.ReadKey(); } /// <summary> /// 假设的网络请求 /// </summary> /// <param name="state">参数</param> private static void MyHttpRequest(object state) { // Thread.Sleep(1000); Console.WriteLine(String.Format("哈哈:{0}",++i)); MutipleThreadResetEvent countdown = state as MutipleThreadResetEvent; //发送信号量 本线程执行完毕 countdown.SetOne(); } } }
.NET4.0
在.NET Framework4.0中,微软为我们提供了CountdownEvent类。
“System.Threading.CountdownEvent 是一个同步基元,它在收到一定次数的信号之后,将会解除对其等待线程的锁定。 CountdownEvent 专门用于以下情况:您必须使用ManualResetEvent 或 ManualResetEventSlim,并且必须在用信号通知事件之前手动递减一个变量。 例如,在分叉/联接方案中,您可以只创建一个信号计数为 5 的CountdownEvent,然后在线程池上启动五个工作项,并且让每个工作项在完成时调用 Signal。 每次调用 Signal 时,信号计数都会递减 1。 在主线程上,对 Wait 的调用将会阻塞,直至信号计数为零。”
下面是微软给出的测试代码:
IEnumerable<Data> source = GetData(); using (CountdownEvent e = new CountdownEvent(1)) { // fork work: foreach (Data element in source) { // Dynamically increment signal count. e.AddCount(); ThreadPool.QueueUserWorkItem(delegate(object state) { try { ProcessData(state); } finally { e.Signal(); } }, element); } e.Signal(); // The first element could be run on this thread. // Join with work. e.Wait(); } // .,.
更多的其它使用方式,请参阅 CountdownEvent。
使用 System.Threading.Tasks.Task类。代码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { Console.WriteLine("Start..."); Task[] tasks = new Task[50]; for (int i = 0; i < 50; i++) { tasks[i] = new Task(new Action<object>((o) => { System.Threading.Thread.Sleep(500); Console.WriteLine(string.Format("{0} is ok", o)); }), i); tasks[i].Start(); } Task.WaitAll(tasks); Console.WriteLine("End\nPress any key to exit"); Console.ReadKey(); } } }
运行时截图如下:
相关文章推荐
- 关于在C#中使用SQL语的中like及使用查询参数的一般做法__net-技术研发_百度空间
- 积分系统方案-关于一般积分系统的整理
- 关于全文搜索的技术方案
- 关于数据库内联接和左联接
- 关于wireshark的Header checksum出问题解决方案
- 关于LIS系统与HIS系统的接口方案
- 关于数据仓库工程师的一般面试题目
- 关于缩放 Windows 窗体 DataGridView 控件的最佳做法的介绍
- SSM框架之关于使用JSP作为视图展示问题解决方案
- 关于GPRS(cmnet、cmwap)和CDMA 1X的比较及最优方案
- 关于实名认证的方案(附代码)
- 解决关于登录校园网显示不在IP段的问题方案(要看注意事项哦!)
- 关于宏定义汇编函数的的做法
- 关于上次说的强类型dataset中事务问题的解决办法,也实用于一般的事务解决方案
- 关于门户网站设计方案
- 关于动态链接库DLL文件的一般常识及常见问题解析
- 第4章 O/R Mapping的一般做法
- 关于SqlDataReader关闭后的connection关闭的做法~
- 关于Zend Studio 配色方案插件的介绍
- 关于IDC机房网络带宽测试方案