在移动交通流调查项目的一个算法分析程序中,碰到一个业务问题:用户采集上传的基站定位数据需要进行分析预处理,方案是先按预定格式解析文件并从中 提取出成百上千个基站定位数据记录,并合并相同的基站点,根据获取到的基站位置信息作为参数,去请求google 基站定位 api,从而得到对应的基站定位经纬度等信息,接下来再加上华工的算法分析。

在执行华工算法分析逻辑之前,调用谷歌api这一步必需全部完成;网络请求是个耗时的过程,故对每一个请求开启单独的线程(同时请求可能数百个,这里通过Semaphore信号量来控制每次发出请求的最大数,该部分的讨论不再本话题之类)。

问题出来了,那么如何知道所有的网络请求全部完成了,可以进行下一步算法分析呢?答案是利用前面讲的ManualResetEvent来处理;于是有下面的写法

1 //针对每个线程 绑定初始化一个ManualResetEvent实例
2 ManualResetEvent doneEvent = new ManualResetEvent(false);
3 //通过ThreadPool.QueueUserWorkItem(网络请求方法HttpRequest,doneEvent ) 来开启多线程
4   
5 //将等待事件一一加入事件列表

01 List<ManualResetEvent> listEvent = new List<ManualResetEvent>(); 
02 for(int i=0;i<请求线程数;i++){
03         listEvent.Add(doneEvent);
04 }
05   
06 //主线程等待网络请求全部完成
07 WaitHandle.WaitAll(listEvent.ToArray());
08 //....接下去的算法分析
09   
10   
11 //在网络请求方法HttpRequest的子线程中调用
12 doneEvent.Set();//通知主线程 本网络请求已经完成

运行好像没有问题,程序按原定计划执行;但是当线程数大于64个之后抛出异常

WaitHandles must be less than or equal to 64

原来WaitHandle.WaitAll(listEvent.ToArray()); 这里listEvent线程数不能超过64个

以前解决方法:

下面是吴建飞以前的方案:既然WaitHandle.WaitAll方法只能唤醒64个ManualResetEvent对象,那么就采用

1 List<List<ManualResetEvent>> _listLocEventList = new List<List<ManualResetEvent>>();

采用这种复杂集合;集合的每个元素也是一个集合(内部每个集合包含最大64个ManualResetEvent对象);和上面一样 把每个线程相关的ManualResetEvent对象添加到该集合;

//主线程等待网络请求全部完成

1 foreach (List<ManualResetEvent> listEvent in _listLocEventList)
2 {
3                 WaitHandle.WaitAll(listEvent.ToArray());
4 }

该方案运用起来比较复杂,而且会导致创建大量的ManualResetEvent对象;

现在的设计目标是这种对文件的分析是多任务同时进行的,也就是说会产生的ManualResetEvent对象List<List<ManualResetEvent>>.Size() * 任务数(N个文件上传)

改进的解决方法:

原理:封装一个ManualResetEvent对象,一个计数器current,提供SetOne和WaitAll方法;

主线程调用WaitAll方法使ManualResetEvent对象等待唤醒信号;

各个子线程调用setOne方法 ,setOne每执行一次current减1,直到current等于0时表示所有子线程执行完毕 ,调用ManualResetEvent的set方法,这时主线程可以执行WaitAll之后的步骤。

目标:减少ManualResetEvent对象的大量产生和使用的简单性。

在这里我写了个封装类:

01 /********************************************************************************
02  * Copyright © 2001 - 2010Comit. All Rights Reserved.
03  * 文件:MutipleThreadResetEvent.cs
04  * 作者:杨柳
05  * 日期:2010年11月13日
06  * 描述:封装 ManualResetEvent ,该类允许一次等待N(N>64)个事件执行完毕
07  
08  *       解决问题:WaitHandle.WaitAll(evetlist)方法最大只能等待64个ManualResetEvent事件
09  * *********************************************************************************/
10 using System;
11 using System.Collections.Generic;
12 using System.Linq;
13 using System.Text;
14 using System.Threading;
15   
16 namespace TestMutipleThreadRestEvent
17 {
18     /// <summary>
19     ///  封装ManualResetEvent
20     /// </summary>
21     public class MutipleThreadResetEvent : IDisposable
22     {
23         private readonly ManualResetEvent done;
24         private readonly int total;
25         private long current;
26   
27         /// <summary>
28         /// 构造函数
29         /// </summary>
30         /// <param name="total">需要等待执行的线程总数</param>
31         public MutipleThreadResetEvent(int total)
32         {
33             this.total = total;
34             current = total;
35             done = new ManualResetEvent(false);
36         }
37   
38         /// <summary>
39         /// 唤醒一个等待的线程
40         /// </summary>
41         public void SetOne()
42         {
43             // Interlocked 原子操作类 ,此处将计数器减1
44             if (Interlocked.Decrement(ref current) == 0)
45             {
46                 //当所以等待线程执行完毕时,唤醒等待的线程
47                 done.Set();
48             }
49         }
50   
51         /// <summary>
52         /// 等待所以线程执行完毕
53         /// </summary>
54         public void WaitAll()
55         {
56             done.WaitOne();
57         }
58   
59         /// <summary>
60         /// 释放对象占用的空间
61         /// </summary>
62         public void Dispose()
63         {
64             ((IDisposable)done).Dispose();
65         }
66     
67   
68 }

注释写的很清楚了:本质就是只通过1个ManualResetEvent 对象就可以实现同步N(N可以大于64)个线程

下面是测试用例:

01 using System;
02 using System.Collections.Generic;
03 using System.Linq;
04 using System.Text;
05 using System.Threading;
06   
07 namespace TestMutipleThreadRestEvent
08 {
09     /// <summary>
10     /// 测试MutipleThreadResetEvent
11     /// </summary>
12     class Program
13     {
14         static int i = 0;
15   
16         /// <summary>
17         /// 主方法
18         /// </summary>
19         /// <param name="args">参数</param>
20         static void Main(string[] args)
21         {
22             //假设有100个请求线程
23             int num = 100;
24   
25             //使用 MutipleThreadResetEvent
26             using (var countdown = new MutipleThreadResetEvent(num))
27             {
28                 for (int i=0;i<num;i++)
29                 {
30                     //开启N个线程,传递MutipleThreadResetEvent对象给子线程
31                     ThreadPool.QueueUserWorkItem(MyHttpRequest, countdown);
32                 }
33   
34                 //等待所有线程执行完毕
35                 countdown.WaitAll();
36             }
37   
38             Console.WriteLine("所有的网络请求以及完毕,可以继续下面的分析...");
39             Console.ReadKey();
40         }
41   
42         /// <summary>
43         /// 假设的网络请求
44         /// </summary>
45         /// <param name="state">参数</param>
46         private static void MyHttpRequest(object state)
47         {
48            // Thread.Sleep(1000);
49             Console.WriteLine(String.Format("哈哈:{0}",++i));
50   
51             MutipleThreadResetEvent countdown = state as MutipleThreadResetEvent;
52             //发送信号量 本线程执行完毕
53             countdown.SetOne();
54         }
55     }
56 }

输出:

      …  省略 ...  

从结果上看线程执行的完成的时间顺序是不固定的;并且只有在所有100个网络请求任务完成后,才显示可以继续下面的分析。

与上面的方案是一样的效果,但是本方案使用非常简单,出错的概念小,免去了创建大量 ManualResetEvent 对象的烦恼

该解决方案可以适用与.net framework 2.0 以上的运行时。

tips:在.net framework 4.0 中有一个CountdownEvent对象可以实现类似的功能;

不过目前公司大多数项目运行时还是基于.net framework 2.0 和 3.5

转载于:https://www.cnblogs.com/xyqCreator/archive/2013/01/10/2854467.html

C#多线程之解决多线程编程中大并发数等待唤醒的问题相关推荐

  1. Java多线程02(线程安全、线程同步、等待唤醒机制)

    Java多线程2(线程安全.线程同步.等待唤醒机制.单例设计模式) 1.线程安全 如果有多个线程在同时运行,而这些线程可能会同时运行这段代码.程序每次运行结果和单线程运行的结果是一样的,而且其他的变量 ...

  2. java基础提升(二):多线程、线程安全、线程状态、等待唤醒机制、线程池

    目录 一. 多线程 1.1并发与并行 1.2 线程与进程 1.3 创建线程类 1.3.1 方式一:继承Thread类 1.3.2 方式二:实现Runnable接口 1.3.3 Thread和Runna ...

  3. rust异步编程--理解并发/多线程/回调/异步/future/promise/async/await/tokio

    1. 异步编程简介 通常我们将消息通信分成同步和异步两种: 同步就是消息的发送方要等待消息返回才能继续处理其它事情 异步就是消息的发送方不需要等待消息返回就可以处理其它事情 很显然异步允许我们同时做更 ...

  4. Java-多线程-Future、FutureTask、CompletionService、CompletableFuture解决多线程并发中归集问题的效率对比

    转载声明 本文大量内容系转载自以下文章,有删改,并参考其他文档资料加入了一些内容: [小家Java]Future.FutureTask.CompletionService.CompletableFut ...

  5. 哎呀,你对高并发与多线程的解决思路了解的还挺深!

    一.大规模并发带来的挑战 在过去的工作中,我曾经面对过5w每秒的高并发秒杀功能,在这个过程中,整个Web系统遇到了很多的问题和挑战.如果Web系统不做针对性的优化,会轻而易举地陷入到异常状态.我们现在 ...

  6. 【Linux网络编程】并发服务器之多线程模型

    00. 目录 文章目录 00. 目录 01. 概述 02. 多线程服务器 03. 多线程服务器实现思路 04. 多线程服务器实现 05. 附录 01. 概述 服务器设计技术有很多,按使用的协议来分有 ...

  7. 解决多线程并发安全问题

    解决多线程的并发安全问题,java无非就是加锁,具体就是两个方法 (1) Synchronized(java自带的关键字) (2) lock 可重入锁 (可重入锁这个包java.util.concur ...

  8. 获取返回值作为变量_解决多线程间共享变量线程安全问题的大杀器——ThreadLocal...

    微信公众号:Zhongger 我是Zhongger,一个在互联网行业摸鱼写代码的打工人! 关注我,了解更多你不知道的[Java后端]打工技巧.职场经验等- 上一期,讲到了关于线程死锁.用户进程.用户线 ...

  9. 解决多线程间共享变量线程安全问题的大杀器——ThreadLocal

    上一期,讲到了关于线程死锁.用户进程.用户线程的相关知识,不记得的小伙伴可以看看:字节跳动面试官问我:你知道线程死锁吗?用户线程.守护线程的概念与区别了解吗? 这期,我们来聊一聊一个在Java并发编程 ...

  10. 基于多线程技术和自定义消息编程实现Windows 9x异步串行通信

    基于多线程技术和自定义消息编程实现Windows 9x异步串行通信 张志明 李蓉艳 王 磊 摘  要  分析了基于Windows 95/98平台上的异步串行通信程序开发方法,并结合开发实践,用C++B ...

最新文章

  1. C#获得网络连接信息 IPGlobalProperties
  2. mysql数据库---授权语句以及备份
  3. 计算机类说课比赛视频,全国“xx杯”计算机类说课大赛课件一等奖作品:制作逐帧动画说课课件...
  4. Java数组学习笔记(遍历、排序、多维数组、命令行参数)
  5. 腾讯专家献上技术干货,带你一览腾讯广告召回系统的演进
  6. nginx+keepalived构建主备负载均衡代理服务器
  7. php中如何判断目录是否存在文件_PHP判断指定目录下是否存在文件
  8. gitee图床失效后处理
  9. 【财政决策支持系统DSS】财政支出OLAP分析模型参考【财政数据仓库DW】
  10. java中什么是实例化
  11. result_of 用法
  12. 常见几种web攻击方式和防御方法
  13. php查找sql,sql如何去重查询
  14. Quartus-II入门实战
  15. linux流量监控php_linux下的几个网络流量监控工具使用的图文教程
  16. 树莓派安装Ubuntu server无屏幕开机自动连接WIFI
  17. 2021年深圳南山区工业企业租金补贴申报时间及条件,补贴300万
  18. 计算机网络 | 网络层 | 什么是网络层 | 详解
  19. error C2059: syntax error : 'type'
  20. 漂浮式半潜风机(一)稳性分析

热门文章

  1. 淘宝新手开店可掌握的爆款核心思路
  2. 解决:“证书错误,导航已阻止”
  3. 谈谈新加坡的电子政务
  4. Vray材质——金属材质
  5. WINCE 矩阵键盘 介绍
  6. 看完《我的前半生》的些许感悟
  7. 随笔20220717
  8. C语言共用体和枚举的使用
  9. 社区垃圾分类AI盒子
  10. 2021年笔迹鉴定收费?江西南昌收费标准是什么?