C#多线程之解决多线程编程中大并发数等待唤醒的问题
在移动交通流调查项目的一个算法分析程序中,碰到一个业务问题:用户采集上传的基站定位数据需要进行分析预处理,方案是先按预定格式解析文件并从中 提取出成百上千个基站定位数据记录,并合并相同的基站点,根据获取到的基站位置信息作为参数,去请求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#多线程之解决多线程编程中大并发数等待唤醒的问题相关推荐
- Java多线程02(线程安全、线程同步、等待唤醒机制)
Java多线程2(线程安全.线程同步.等待唤醒机制.单例设计模式) 1.线程安全 如果有多个线程在同时运行,而这些线程可能会同时运行这段代码.程序每次运行结果和单线程运行的结果是一样的,而且其他的变量 ...
- java基础提升(二):多线程、线程安全、线程状态、等待唤醒机制、线程池
目录 一. 多线程 1.1并发与并行 1.2 线程与进程 1.3 创建线程类 1.3.1 方式一:继承Thread类 1.3.2 方式二:实现Runnable接口 1.3.3 Thread和Runna ...
- rust异步编程--理解并发/多线程/回调/异步/future/promise/async/await/tokio
1. 异步编程简介 通常我们将消息通信分成同步和异步两种: 同步就是消息的发送方要等待消息返回才能继续处理其它事情 异步就是消息的发送方不需要等待消息返回就可以处理其它事情 很显然异步允许我们同时做更 ...
- Java-多线程-Future、FutureTask、CompletionService、CompletableFuture解决多线程并发中归集问题的效率对比
转载声明 本文大量内容系转载自以下文章,有删改,并参考其他文档资料加入了一些内容: [小家Java]Future.FutureTask.CompletionService.CompletableFut ...
- 哎呀,你对高并发与多线程的解决思路了解的还挺深!
一.大规模并发带来的挑战 在过去的工作中,我曾经面对过5w每秒的高并发秒杀功能,在这个过程中,整个Web系统遇到了很多的问题和挑战.如果Web系统不做针对性的优化,会轻而易举地陷入到异常状态.我们现在 ...
- 【Linux网络编程】并发服务器之多线程模型
00. 目录 文章目录 00. 目录 01. 概述 02. 多线程服务器 03. 多线程服务器实现思路 04. 多线程服务器实现 05. 附录 01. 概述 服务器设计技术有很多,按使用的协议来分有 ...
- 解决多线程并发安全问题
解决多线程的并发安全问题,java无非就是加锁,具体就是两个方法 (1) Synchronized(java自带的关键字) (2) lock 可重入锁 (可重入锁这个包java.util.concur ...
- 获取返回值作为变量_解决多线程间共享变量线程安全问题的大杀器——ThreadLocal...
微信公众号:Zhongger 我是Zhongger,一个在互联网行业摸鱼写代码的打工人! 关注我,了解更多你不知道的[Java后端]打工技巧.职场经验等- 上一期,讲到了关于线程死锁.用户进程.用户线 ...
- 解决多线程间共享变量线程安全问题的大杀器——ThreadLocal
上一期,讲到了关于线程死锁.用户进程.用户线程的相关知识,不记得的小伙伴可以看看:字节跳动面试官问我:你知道线程死锁吗?用户线程.守护线程的概念与区别了解吗? 这期,我们来聊一聊一个在Java并发编程 ...
- 基于多线程技术和自定义消息编程实现Windows 9x异步串行通信
基于多线程技术和自定义消息编程实现Windows 9x异步串行通信 张志明 李蓉艳 王 磊 摘 要 分析了基于Windows 95/98平台上的异步串行通信程序开发方法,并结合开发实践,用C++B ...
最新文章
- C#获得网络连接信息 IPGlobalProperties
- mysql数据库---授权语句以及备份
- 计算机类说课比赛视频,全国“xx杯”计算机类说课大赛课件一等奖作品:制作逐帧动画说课课件...
- Java数组学习笔记(遍历、排序、多维数组、命令行参数)
- 腾讯专家献上技术干货,带你一览腾讯广告召回系统的演进
- nginx+keepalived构建主备负载均衡代理服务器
- php中如何判断目录是否存在文件_PHP判断指定目录下是否存在文件
- gitee图床失效后处理
- 【财政决策支持系统DSS】财政支出OLAP分析模型参考【财政数据仓库DW】
- java中什么是实例化
- result_of 用法
- 常见几种web攻击方式和防御方法
- php查找sql,sql如何去重查询
- Quartus-II入门实战
- linux流量监控php_linux下的几个网络流量监控工具使用的图文教程
- 树莓派安装Ubuntu server无屏幕开机自动连接WIFI
- 2021年深圳南山区工业企业租金补贴申报时间及条件,补贴300万
- 计算机网络 | 网络层 | 什么是网络层 | 详解
- error C2059: syntax error : 'type'
- 漂浮式半潜风机(一)稳性分析