why happen "WaitHandles must be less than or equal to 64"
一、背景:
在一个项目中碰到大数据插入的问题,一次性插入20万条数据(SQL Server),并用200个线程去执行,计算需要花费多少时间,因此需要等200个线程处理完成后,记录花费的时间,需要考虑的一个问题是:如何判断判断多个线程是否全部执行完成。在执行数据库的插入过程中,当每个线程需要处理的数据量大时,是个耗时的过程,故对通过配置开启多个线程。
二、问题:
问题出来了,那么如何知道所有的线程操作都全部完成了,答案是利用C#中的的ManualResetEvent来处理;于是有下面的写法。
//针对每个线程 绑定初始化一个ManualResetEvent实例
ManualResetEvent doneEvent = new ManualResetEvent(false);
//通过ThreadPool.QueueUserWorkItem(网络请求方法HttpRequest,doneEvent ) 来开启多线程 //将等待事件一一加入事件列表 List<ManualResetEvent> listEvent = new List<ManualResetEvent>();
for(int i=0;i<线程数;i++){
listEvent.Add(doneEvent);
} //主线程等待每个线程全部完成
WaitHandle.WaitAll(listEvent.ToArray());
//....接下去的时间计算 //在保存数据的的每个线程中调用
doneEvent.Set();//通知主线程 本线程保存数据方法已经调用完成
运行好像没有问题,但是当线程数大于64个之后抛出异常 WaitHandles must be less than or equal to 64
通过网上查询,得知原来WaitHandle.WaitAll(listEvent.ToArray()); 这里listEvent线程数不能超过64个
三、解决方案:
原理:封装一个ManualResetEvent对象,一个计数器current,提供SetOne和WaitAll方法;
主线程调用WaitAll方法使ManualResetEvent对象等待唤醒信号;
各个子线程调用setOne方法 ,setOne每执行一次current减1,直到current等于0时表示所有子线程执行完毕 ,调用ManualResetEvent的set方法,这时主线程可以执行WaitAll之后的步骤。
目标:减少ManualResetEvent对象的大量产生和使用的简单性。
四、例子:
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) == )
{
//当所以等待线程执行完毕时,唤醒等待的线程
done.Set();
}
} /// <summary>
/// 等待所以线程执行完毕
/// </summary>
public void WaitAll()
{
done.WaitOne();
} /// <summary>
/// 释放对象占用的空间
/// </summary>
public void Dispose()
{
((IDisposable)done).Dispose();
}
}
本质就是只通过1个ManualResetEvent 对象就可以实现同步N(N可以大于64)个线程
public class Process
{
public static int workItemCount = ;
Process() { }
public static void ProcessDataThread(object state)
{
State stateinfo = state as State; int count = Int32.Parse(stateinfo.perthreadtotal.ToString());
if (count == )
{
ProcessManage.InputLog("输入的数据不正确,请重新输入");
return;
}
int workItemNumber = workItemCount;
Interlocked.Increment(ref workItemCount);
ProcessManage.InputLog(string.Format("线程{0}开始工作", workItemNumber.ToString()));
string sql = @"insert into gpsposition (PlateType,CarNo,Latitude,Longitude,Altitude,Heading,Speed,Timestamp)
values";
string insertvalue = string.Empty;
string va = @"('02','渝B12345',10,10,10,20,10,'2016/11/1 12:00'),";
for (int i = ; i < count; i++)
{
insertvalue = (insertvalue + va);
}
insertvalue = insertvalue.TrimEnd(',');
//执行sql语句
try
{
ProcessManage.ProcessData(string.Concat(sql, insertvalue));
}
catch (Exception ex)
{
ProcessManage.InputLog(string.Format("线程{0},SQL执行错误:{1}", workItemNumber.ToString(), ex.Message));
}
finally
{
ProcessManage.InputLog(string.Format("线程{0}执行完成", workItemNumber.ToString()));
stateinfo.manualEvent.SetOne();
}
}
}
页面调用代码如下:
private void button1_Click(object sender, EventArgs e)
{
Process.workItemCount = ;
int threadcount = ;
int totalcount = ;
Int32.TryParse(this.txtThreadCount.Text, out threadcount);
Int32.TryParse(this.txtTotalCount.Text, out totalcount);
if (threadcount == || totalcount == )
{
MessageBox.Show("文本中输入的数字不正确,请输入大于0的整数");
return;
}
int perthreadtotal = totalcount / threadcount;
ProcessManage.InputLog(string.Format("总记录数-{0}条", totalcount));
ProcessManage.InputLog(string.Format("线程执行数量-{0}个", threadcount));
ProcessManage.InputLog(string.Format("每个线程执行记录数-{0}条", perthreadtotal));
ProcessManage.InputLog("=================================================");
ProcessManage.InputLog("开始启动线程执行"); State stateInfo;
Stopwatch watch = new Stopwatch();
watch.Start();
using (var manualEvents = new MutipleThreadResetEvent(threadcount))
{
for (int i = ; i < threadcount; i++)
{
stateInfo = new State(manualEvents, perthreadtotal);
ThreadPool.QueueUserWorkItem(new WaitCallback(Process.ProcessDataThread), stateInfo);
}
manualEvents.WaitAll();
}
watch.Stop();
ProcessManage.InputLog(string.Format("全部线程执行完成,耗时{0}秒",watch.Elapsed));
}
五、UI效果:



总结:20万数据一次性用200个线程执行,只花费了5秒多的时间即完成。
why happen "WaitHandles must be less than or equal to 64"的更多相关文章
- Reloading Java Classes 201: How do ClassLoader leaks happen? Translation
The original link : http://zeroturnaround.com/rebellabs/rjc201/ From ClassLoaders to Classes 从ClassL ...
- English trip V1 - 6.Accidents Happen! 发生意外! Teacher:Corrine Key: 过去进行时 was or were + Ving
In this lesson you will learn to talk about past occurences. 过去进行时 课上内容(Lesson) C: Hi, Loki! L: Hi, ...
- 【MyEcplise】导入项目后,会定时弹出一下错误MyEcplise tern was unable to complete your request in time.This couble happen if your project contains several large javaScript libraies.
Myecplise弹出错误如下: 错误代码: MyEcplise tern was unable to complete your request in time.This couble happen ...
- happen before 原则
并发一直都是程序开发者绕不开的难题,在上一篇文章中我们知道了导致并发问题的源头是 : 多核 CPU 缓存导致程序的可见性问题.多线程间切换带来的原子性问题以及编译优化带来的顺序性问题. 原子性问题我们 ...
- jvm(三)指令重排 & 内存屏障 & 可见性 & volatile & happen before
参考文档: https://tech.meituan.com/java-memory-reordering.html http://0xffffff.org/2017/02/21/40-atomic- ...
- 【JS】Beginner1:Making Stuff Happen
1.JS(JavaScript) is for interactivity 2.How does JS relate to HTML&CSS? script tag script elemen ...
- In ZeroDB, the client is responsible for the database logic. Data encryption, decryption, and compression also happen client side. Therefore, the server never has any knowledge about the data, its str
zerodb/index.rst at master · zerodb/zerodb https://github.com/zerodb/zerodb/blob/master/docs/source/ ...
- 进程间IPC通信-stop waiting for thing to happen,go out and make them happen!!!
进程间通信: System V IPC对象: ipcs -q:查看消息队列 ipcs -m:查看共享内存 ipcs -s:查看信号灯集 ipcrm -q:删除消息队列 ipcrm -m:删除共 ...
- 从netty-example分析Netty组件续
上文我们从netty-example的Discard服务器端示例分析了netty的组件,今天我们从另一个简单的示例Echo客户端分析一下上个示例中没有出现的netty组件. 1. 服务端的连接处理,读 ...
随机推荐
- 操作系统学习笔记(五)--CPU调度
由于第四章线程的介绍没有上传视频,故之后看书来补. 最近开始学习操作系统原理这门课程,特将学习笔记整理成技术博客的形式发表,希望能给大家的操作系统学习带来帮助.同时盼望大家能对文章评论,大家一起多多交 ...
- WebStorm里面配置运行React Native的方案
以前开发react native项目总是需要打开WebStorm编写代码,Xcode跑项目.显得有点多余. 今天教大家如何直接使用WebStorm这个IDE直接完成编码+运行项目工作.这样就可以不用打 ...
- 连接WCF报EntityFramework.SqlServer 错误的解决方法
现象: The Entity Framework provider type 'System.Data.Entity.SqlServer.SqlProviderServices, EntityFram ...
- Html概要及示例(一)
Html 简介 Html 是一种超文本标记语言,标记往往成对出现,例如 段落标记<p> 层标记 <div> 以及 <marqueen>等等,标记繁多 但未必就要全 ...
- Init
alloc负责分配对象空间,init负责初始化对象.init是实例方法,返回的是初始化后的对象的地址.init是NSObject的初始化方法. 子类不实现init,会执行由NSObject定义的ini ...
- secureCRT的一些小知识
secureCRT 是一个非常不错的终端软件,在嵌入式开发过程中经常使用到,所以了解一下其快捷键操作是非常有必要的,可以提高开发效率. 0.在secureCRT里切换不同的窗口:ctrl+tab. ...
- 关于分开编写多个LaTeX文件的一点微小的总结
[转载请注明出处]http://www.cnblogs.com/mashiqi 2016/11/05 在编写LaTeX文档的时候,由于文档的section较多,或者section的编写时间各不相同,我 ...
- 排球比赛计分规则(P205页)
排球比赛计分规则: 1.SPEC的目标是什么?SPEC的目标不包括什么? 为了让大家更加了解排球比赛. 2.SPEC用户和典型场景是什么? 用户:运动员,观众,教练 场景:排球赛场 3.SPEC用到哪 ...
- ImageLoader框架的使用、调用系统相册显示图片并裁剪显示、保存图片的两种方式
ImageLoader虽然说是一个相对于比较老的一个框架了 ,但是总的来说,还是比较好用的,今天我就总结了一下它的用法.还有调用系统相册并裁剪,以及,通过sharedpreference和文件存储来保 ...
- js 基础面试题
function printArray(arr){ for(var i in arr){ if(arr[i] instance of Array){ printArray(arr[i]); }else ...