C#读书笔记之并行任务
这两天遇到一个多线程任务优化的问题,现在解决了,把心得用例子的形式记下来。
假设有四个任务:
任务1:登陆验证(CheckUser)
任务2:验证成功后从Web服务获取数据(GetDataFromWeb)
任务3:验证成功后从数据库获取数据(GetDatFromDb)
任务4:使用2、3的数据执行一个方法 (StartProcess)
一个比较笨的方法(本人最开始的方法,记为方法1)是直接开启一个线程,按照顺序依次执行四个任务:
new Thread(delegate
{
CheckUser();
GetDatFromDb();//从数据库获取数据
GetDataFromWeb();//web服务获取数据
StartProcess();//执行4
}).Start();
但是仔细分析需求我们会发现,任务2和任务3并没有先后区别,事实上两者并无关联,只不过任务4的执行需要任务2和3都已完成作为条件,所以我们可以再开两个线程用于执行任务2和任务3,当两者都执行完毕之后,执行任务4。
在这里使用了两个全局变量用于表示任务2和任务3的状态。用三个线程分别执行任务2、3、4,其中任务4一直在循环监听全局变量的状态,确保在2、3都执行完毕后才执行。
这记为方法2:
private static volatile bool _m2;//任务2的标志位
private static volatile bool _m3;//任务3的标志位
private static void Main(string[] args)
{
new Thread(delegate
{
CheckUser();
new Thread(delegate
{
GetDatFromDb();//从数据库获取数据
_m2 = true;//标志位置为true
}).Start();
new Thread(delegate
{
GetDataFromWeb();//web服务获取数据
_m3 = true;//标志位置为true
}).Start();
new Thread(delegate
{
while (!(_m3 && _m2))//判断任务2和3是否已执行完毕
{
Thread.Sleep();
}
StartProcess();//执行任务4
_m2 = true;
}).Start();
}).Start();
}
以上代码基本上已经可以达到预期目标了,但是由于借助了两个全局变量,尽管在这里不会涉及到同步冲突的问题,但总觉得很不放心,而且当我们需要做扩展的时候,比方说在执行任务4之前,我们还需要加载文件内的数据(GetDataFromFile),那我们必须再添加一个全局标志位,显得有点麻烦。
事实上,Thread类本身已经拥有对这种情况的完美解决方案——join。
Thread.Join 方法
在继续执行标准的 COM 和 SendMessage 消息泵处理期间,阻塞调用线程,直到某个线程终止为止。
简单来说,join就是个阻塞方法,在线程1内创建线程2,调用线程2的Join方法,那么线程1将会被阻塞,直到线程2执行完毕。运用到上面的例子就是,在任务4内创建任务2和任务3的线程,调用任务2和任务3的线程的Join方法使任务4阻塞,直到任务2和任务3执行完毕,才继续执行任务4。这记为方法3,代码如下
private static void Main(string[] args)
{
new Thread(delegate
{
CheckUser();
new Thread(delegate
{
Thread task2 = new Thread(delegate
{
GetDatFromDb(); //从数据库获取数据
});
Thread task3 = new Thread(delegate
{
GetDataFromWeb(); //web服务获取数据
});
task2.Start();
task3.Start();
task2.Join();//任务2阻塞
task3.Join();//任务3阻塞
StartProcess(); //执行任务4
}).Start();
}).Start();
}
这样便不需要任何标志位了。这是最理想的解决方案。
另外还有一种解决方案,使用EventWaitHandle
EventWaitHandle 类允许线程通过发出信号和等待信号来互相通信。事件等待句柄(简称事件)就是可以通过发出相应的信号来释放一个或多个等待线程的等待句柄。信号发出后,可以用手动或自动方式重置事件等待句柄
简单来说,就是方法2的进阶版,使用EventWaitHandle控制状态,而不再使用While循环监听,但这里仍旧需要两个全局的EventWaitHandle对象。该方法记为方法4,代码如下
private static EventWaitHandle eventWait1 = new EventWaitHandle(false, EventResetMode.AutoReset);//初始化状态false;
private static EventWaitHandle eventWait2 = new EventWaitHandle(false, EventResetMode.AutoReset);//初始化状态false;
private static void Main(string[] args)
{
new Thread(delegate
{
CheckUser();
new Thread(delegate
{
GetDatFromDb(); //从数据库获取数据
eventWait1.Set(); //标志位置为true
}).Start();
new Thread(delegate
{
GetDataFromWeb(); //web服务获取数据
eventWait2.Set(); //标志位置为true
}).Start();
new Thread(delegate
{
eventWait1.WaitOne();//任务2阻塞,等待
eventWait2.WaitOne();//任务3阻塞,等待
StartProcess(); //执行任务4
}).Start();
}).Start();
}
上述三个优化方案,其实核心思想都是一样的,都是通过开启3个线程分别执行2、3、4任务,其中任务4被阻塞(while循环、eventWait.WaitOne,thread.join),当阻塞解除后,继续执行任务4。也就是说,任务4,其实是一直在等待任务2和任务3的完成。那么,是否有办法让任务2和任务3主动通知任务4呢?即,任务2和任务3完成后,主动执行任务4。
方法当然有:异步委托+回调函数
private static object obj = new object();
private static volatile bool _m2;//任务2的标志位
private static volatile bool _m3;//任务3的标志位 private static void Main(string[] args)
{
CheckUser(); //第一步 验证用户
Action step2 = delegate
{
GetDatFromDb(); //从数据库获取数据
_m2 = true; //标志位置为true
};
Action step3 = delegate
{
GetDataFromWeb(); //web服务获取数据
_m3 = true; //标志位置为true
}; step2.BeginInvoke(delegate
{
if (_m2 && _m3) //通过标志位判断2 3是否都已完成
{
lock (obj)//加锁
{
_m2 = false;
if (_m3)//二重验证 防止两者同时进入
StartProcess(); //执行4
}
}
}, null);
step3.BeginInvoke(delegate
{
if (_m2 && _m3) //通过标志位判断2 3是否都已完成
{
lock (obj)
{
_m3 = false;
if (_m2)
StartProcess(); //执行4
}
}
}, null);
}
讲解下代码。首先以委托的方式创建了任务2和任务3的委托对象step2和step3。执行这两个委托的异步调用方法BegInvoke。执行BegInvoke,会创建一个新的线程来执行step2和step3的方法,同时,在执行BeginInvoke的时候还指定了一个回调函数
delegate
{
if (_m2 && _m3) //通过标志位判断2 3是否都已完成
{
lock (obj)
{
_m3 = false;
if (_m2)
StartProcess(); //执行4
}
}
}
这个函数会在step2和step3的线程执行完毕后被调用。在这里,我再次使用了标志位来判断step2和step3是否已经运行完成,同时,为了防止一种特殊情:“step2和step3所执行的时间几乎相等,他们会同时通过if(_m2&&_m3)判断,进而执行两次StartProcess” 在这里加了lock锁,并且在lock锁内将标志位重置+二重判断(这里可以参考单例模式的双重锁定原理),确保StarProcess只会执行一次。
如此这般,一个主动通知模式的并行任务便实现了,不过,这种实现方法相较于方法2,实在太过麻烦,尤其在与并发处理方面,个人感觉实用性不太高。
另外,还可以使用观察者模式实现异步委托+回调函数的效果,具体代码就不写了。
C#读书笔记之并行任务的更多相关文章
- 《C#本质论》读书笔记(18)多线程处理
.NET Framework 4.0 看(本质论第3版) .NET Framework 4.5 看(本质论第4版) .NET 4.0为多线程引入了两组新API:TPL(Task Parallel Li ...
- 《android开发艺术探索》读书笔记(十一)--Android的线程和线程池
接上篇<android开发艺术探索>读书笔记(十)--Android的消息机制 No1: 在Android中可以扮演线程角色的有很多,比如AsyncTask.IntentService.H ...
- 《Java编程思想》读书笔记(五)
前言:本文是<Java编程思想>读书笔记系列的最后一章,本章的内容很多,需要细读慢慢去理解,文中的示例最好在自己电脑上多运行几次,相关示例完整代码放在码云上了,码云地址:https://g ...
- 读书笔记汇总 - SQL必知必会(第4版)
本系列记录并分享学习SQL的过程,主要内容为SQL的基础概念及练习过程. 书目信息 中文名:<SQL必知必会(第4版)> 英文名:<Sams Teach Yourself SQL i ...
- 读书笔记--SQL必知必会18--视图
读书笔记--SQL必知必会18--视图 18.1 视图 视图是虚拟的表,只包含使用时动态检索数据的查询. 也就是说作为视图,它不包含任何列和数据,包含的是一个查询. 18.1.1 为什么使用视图 重用 ...
- C#温故知新:《C#图解教程》读书笔记系列
一.此书到底何方神圣? 本书是广受赞誉C#图解教程的最新版本.作者在本书中创造了一种全新的可视化叙述方式,以图文并茂的形式.朴实简洁的文字,并辅之以大量表格和代码示例,全面.直观地阐述了C#语言的各种 ...
- C#刨根究底:《你必须知道的.NET》读书笔记系列
一.此书到底何方神圣? <你必须知道的.NET>来自于微软MVP—王涛(网名:AnyTao,博客园大牛之一,其博客地址为:http://anytao.cnblogs.com/)的最新技术心 ...
- Web高级征程:《大型网站技术架构》读书笔记系列
一.此书到底何方神圣? <大型网站技术架构:核心原理与案例分析>通过梳理大型网站技术发展历程,剖析大型网站技术架构模式,深入讲述大型互联网架构设计的核心原理,并通过一组典型网站技术架构设计 ...
- LOMA280保险原理读书笔记
LOMA是国际金融保险管理学院(Life Office Management Association)的英文简称.国际金融保险管理学院是一个保险和金融服务机构的国际组织,它的创建目的是为了促进信息交流 ...
随机推荐
- POJ C程序设计进阶 编程题#3:寻找山顶
编程题#3:寻找山顶 来源: POJ (Coursera声明:在POJ上完成的习题将不会计入Coursera的最后成绩.) 注意: 总时间限制: 1000ms 内存限制: 65536kB 描述 在一个 ...
- 查看Linux系统版本信息
一.查看Linux内核版本命令(两种方法): 1.cat /proc/version [root@S-CentOS home]# cat /proc/versionLinux version 2.6. ...
- Emmet用法
Emmet的前身是大名鼎鼎的Zen coding,如果你从事Web前端开发的话,对该插件一定不会陌生.它使用仿CSS选择器的语法来生成代码,大大提高了HTML/CSS代码编写的速度,比如下面的演示: ...
- 日期转换(用DateTime的ParseExact方法解析特殊的日期时间)
今天遇到一个特别的需求,需要从下面的字符串中转换成一个DateTime对象: [07-13 15:50:42] 主要问题是这个时间不是标准的时间,而是自定义的格式,即开头是月-日,然后是时间. 使用最 ...
- 小菜的系统框架界面设计-灰姑娘到白雪公主的蜕变(工具条OutLookBar)
灰姑娘本身也有自已的优点,但是却可能因为外貌不讨人喜欢,要变成白雪公主却需要有很多勇气和决心去改变自已: 有一颗善良的心 讨人喜爱的外貌 --蜕变--> 我这里讲的是一个工具条的蜕变过程, ...
- 使用虚拟机在ubuntu下搭建mongoDB开发环境和简单增删改查操作
最近在折腾mongodb和nodejs,在imooc上找了一个mongodb的入门教程,跟着里面一步一步的走,下面记录下我操作的步骤和遇到的问题. 课程地址:http://www.imooc.com/ ...
- C#生成不重复随机数列表
C#生成不重复(随机数 http://www.jbxue.com/tags/suijishu.html)列表实例的代码.例子: ; Random rnd = ; i < ...
- 一款jQuery特效编写的大度宽屏焦点图切换特效
一款jQuery编写的大度宽屏焦点图切换特效 焦点图显示区域有固定的宽度,当前显示宽度之外是一个半透明层显示的其它的焦点图片, 最好的是,此特效兼容IE6以及其它浏览器. 适用浏览器:IE6.IE7. ...
- css 串联选择器和后代选择器
串联选择器:作用在同一个标签上 <div class=”a” id ="qq"><span>look at the color</span>&l ...
- programming ruby
ri #rdoc reader attr_reader attr_writer @@xx 类变量都是私有的 def 类名.xx end 类方法 [1,3,5,7].inject(0){|sum,e| ...