.NET 实现并行的几种方式(二)
本随笔续接:.NET 实现并行的几种方式(一)
四、Task
3)Task.NET 4.5 中的简易方式
在上篇随笔中,两个Demo使用的是 .NET 4.0 中的方式,代码写起来略显麻烦,这不 .NET 4.5提供了更加简洁的方式,让我们来看一下吧。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/// <summary> /// Task.NET 4.5 中的简易方式 /// </summary> public void Demo3() { Task.Run(() => { SetTip("简洁的代码"); }); Task.Run(() => { SetTip("验证 CreationOptions 属性"); }).ContinueWith((t)=> { SetTip("CreationOptions:" + t.CreationOptions.ToString()); }); }
五、TPL (Task Parallel Library)
TPL (任务并行库)是 .NET 4.0 中的另一个重量级模块,可以极其优雅、便捷地完成并行逻辑的编码工作。
1)Parallel.Invoke并行多个独立的Action
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/// <summary> /// Parallel.Invoke并行多个独立的Action /// </summary> public void Demo1() { Task.Run(() => { List<Action> actions = new List<Action>(); // 生成并行任务 for (int i = 0; i < 5; i++) { // 注意、这里很关键,不可直接使用i变量。 // 原因在稍后的随笔中进行说明 int index = i; actions.Add(new Action(() => { SetTip(string.Format("Task{0} 开始", index)); SetTip(string.Format("Task{0} 休眠1秒", index)); Thread.Sleep(1000); SetTip(string.Format("Task{0} 休眠5秒", index)); Thread.Sleep(5000); SetTip(string.Format("Task{0} 结束", index)); })); } // 执行并行任务 Parallel.Invoke(actions.ToArray()); // 当上述的5个任务全部执行完毕后,才会执行该代码 SetTip("并行任务执行完毕"); }); }
2)Parallel简单的For并行
如果 Parallel.Invoke 看做是任务并行, 则 Parallel.For 则是数据并行,可方便的完成For循环并行遍历。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/// <summary> /// Parallel简单的For并行 /// </summary> public void Demo2() { // 为了实时更新UI、将代码异步执行 Task.Run(() => { Parallel.For(1, 100, (index) => { SetTip(string.Format("Index:{0}, 开始执行Task", index)); Thread.Sleep(1000); SetTip(string.Format("Index:{0}, 开始休眠Action 1秒", index)); SetTip(string.Format("Index:{0}, Task执行完毕", index)); }); SetTip("并行任务执行完毕"); }); }
3)Parallel.For并行 并行中的 break、 return、 continue
break : 在 Parallel.For 中使用 ParallelLoopState.Break() 方法代替。
return: 在 Parallel.For 中使用 ParallelLoopState.Break() 方法代替。
continue : 在 Parallel.For 中直接使用 return 即可。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/// <summary> /// 中断Parallel.For并行 /// </summary> public void Demo3() { // 为了实时更新UI、将代码异步执行 Task.Run(() => { int breakIndex = new Random().Next(10, 50); SetTip(" BreakIndex : -------------------------" + breakIndex); Parallel.For(1, 100, (index, state) => { SetTip(string.Format("Index:{0}, 开始执行Task", index)); if (breakIndex == index) { SetTip(string.Format("Index:{0}, ------------------ Break Task", index)); state.Break(); // Break方法执行后、 // 大于 当前索引的并且未被安排执行的迭代将被放弃 // 小于 当前索引的的迭代将继续正常执行直至迭代执行完毕 return; } Thread.Sleep(1000); SetTip(string.Format("Index:{0}, 休眠Action 1秒", index)); SetTip(string.Format("Index:{0}, Task执行完毕", index)); }); SetTip("并行任务执行完毕"); }); } /// <summary> /// 终止Parallel.For并行 /// </summary> public void Demo4() { // 为了实时更新UI、将代码异步执行 Task.Run(() => { int stopIndex = new Random().Next(10, 50); SetTip(" StopIndex : -------------------------" + stopIndex); Parallel.For(1, 100, (index, state) => { SetTip(string.Format("Index:{0}, 开始执行Task", index)); if (stopIndex == index) { SetTip(string.Format("Index:{0}, ------------------ Stop Task", index)); state.Stop(); // Stop方法执行后 // 整个迭代将被放弃 return; } Thread.Sleep(1000); SetTip(string.Format("Index:{0}, 休眠Action 1秒", index)); SetTip(string.Format("Index:{0}, Task执行完毕", index)); }); SetTip("并行任务执行完毕"); }); }
4)Parallel.For并行中的数据聚合
在并行中,绝大多数委托都是在不同的线程中运行的,如果需要在并行中进行的数据共享、则需要考虑线程同步问题,然而线程同步会影响并行性能。
为了解决特定情况下的数据共享,而又不会因为线程同步而影响性能,Parallel.For 提供了解决方案:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/// <summary> /// Parallel.For并行中的数据聚合 /// </summary> public void Demo5() { Task.Run(() => { // 求 1 到 10 的阶乘的 和 long total = 0; Parallel.For<long>(1, 10, () => { SetTip("LocalInit"); return 0; }, (index, state, local) => { SetTip("Body"); int result = 1; for (int i = 2; i < index; i++) { result *= i; } local += result; return local; }, (x) => { SetTip("LocalFinally"); Interlocked.Add(ref total, x); }); SetTip("Total : " + total); SetTip("并行任务执行完毕"); }); }
MSDN备注:
对于参与循环执行的每个线程调用 LocalInit 委托一次,并返回每个线程的初始本地状态。
这些初始状态传递到每个线程上的第一个 body 调用。 然后,每个后续正文调用返回可能修改过的状态值,传递到下一个正文调用。
最后,每个线程上的最后正文调用返回传递给 LocalFinally 委托的状态值。
每个线程调用 localFinally 委托一次,以对每个线程的本地状态执行最终操作。
此委托可以被多个线程同步调用;因此您必须同步对任何共享变量的访问。
也就是说:
1) 并行中开辟的线程数 决定了 LocalInit、LocalFinally 的调用次数
2) 多个 迭代委托、Body 可能被同一个线程调用。
3) 迭代委托、Body 中的 local值,并不一定是 LocalInit 的初始值,也有可能是被修改的返回值。
4) LocalFinally 可能是被同时调用的,需要注意线程同步问题。
5)Parallel.ForEach并行
Parallel.ForEach算是另一种数据并行方式, 它与大家熟知的 IEnumerable<TSource> 接口结合十分紧密,是 foreach的并行版本。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/// <summary> /// Parallel.ForEach并行 /// </summary> public void Demo6() { Task.Run(() => { Parallel.ForEach<int>(Enumerable.Range(1, 10), (num) => { SetTip("Task 开始"); SetTip("Task 休眠" + num + "秒"); Thread.Sleep(TimeSpan.FromSeconds(num)); SetTip("Task 结束"); }); SetTip("并行任务执行完毕"); }); }
6)Parallel.ForEach中的索引,中断、终止操作
在 Parallel.ForEach 中也可以轻易的获得其遍历的索引
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/// <summary> /// Parallel.ForEach中的索引,中断、终止操作 /// </summary> public void Demo7() { Task.Run(() => { Parallel.ForEach<int>(Enumerable.Range(0, 10), (num, state, index) => { // num, 并行数据源中的数据项 // state, SetTip(" Index : " + index + " Num: " + num); }); SetTip("并行任务执行完毕"); }); }
本随笔到此、暂告一段落。
附,Demo : https://files.cnblogs.com/files/08shiyan/ParallelDemo.zip
参见更多:随笔导读:同步与异步
(未完待续...)
相关文章
- ASP .NET 如何在 SQL 查询层面实现分页
- Silverlight实用窍门系列:37.Silverlight和ASP.NET相互传参的两种常用方式(QueryString,Cookie)【附带实例源码】
- ASP.NET WebAPI如何实现文件的上传实战演练
- ASP.NET Core基于SignalR实现消息推送实战演练
- C#.NET常见问题(FAQ)-控制台程序如何输出Messagebox
- ASP.NET利用JQuery实现AJAX(前台脚本代码)调用后台静态方法
- ASP.NET中IHttpHandler与IHttpModule的区别(带样例说明)
- 二叉排序树C实现(含完整源码) - 兰亭风雨的专栏 - 博客频道 - CSDN.NET
- c# .net缓存(旧)
- golang net/http 包
- [ASP.NET Core MVC] 如何实现运行时动态定义Controller类型?
- 通过极简模拟框架让你了解ASP.NET Core MVC框架的设计与实现[中篇]:请求的响应
- [ASP.NET Core 3框架揭秘] 依赖注入[9]:实现概述
- ASP.NET Core管道深度剖析(1):采用管道处理HTTP请求
- Atitit. 获取cpu占有率的 java c# .net php node.js的实现
- Atitit. 悬浮窗口的实现 java swing c# .net c++ js html 的实现
- atitit.解决net.sf.json.JSONException There is a cycle in the hierarchy
- .Net——实现IConfigurationSectionHandler接口定义处理程序处理自己定义节点
- Asp.NET MVC3 使用 SignalR 实现推(持续)
- .Net Reactor 单个dll或exe文件的保护
- C# 使用ZXing.NET生成一维码、二维码
- . net core的发布
- .NET深入解析LINQ框架(三:LINQ优雅的前奏)
- 关于ASP.net开启身份验证时集成的托管管道模式下不适用的 ASP.NET 设置