【C#】线程之Parallel
在一些常见的编程情形中,使用任务也许能提升性能。为了简化变成,静态类System.Threading.Tasks.Parallel封装了这些常见的情形,它内部使用Task对象。
Parallel.For & Parallel.Foreach & Pararllel.Invoke
Parallel.For(, , (i) =>
{
//i是从0开始一直到1000结束 }); var lst = new List<string>(); Parallel.ForEach(lst, (s) =>
{
//do something
}); //Invoke
Parallel.Invoke(
() => { },
() => { },
() => { }
);
三个不同的方式做并行操作,Invoke是自己定义的并行,像上面的代码,只会开三个线程去做,而For和Foreach和普通的没什么区别只是并行去做了.
这里需要注意的是:这个三个方法都会堵塞当前线程,要等待所有线程都做完了以后才能往下执行,这是有用的,当进行数据分析的时候,每条数据都是独立的,这个时候等待所有线程做完是有意义的,而如果需要不堵塞线程可以用TASK来包一层,具体请参照【C#】线程之Task。在并发的时候如果有某些线程出现了异常,这个时候不会中断线程,会在所有线程结束的时候抛出AggregateException(并不是所有异常都能用这个捕获),我们通过这个异常可以知道所有的异常:
Parallel.ForEach(new List<string>
{
"aa",
"bb",
"cc",
"dd",
"ee",
}, (b) =>
{
Thread.Sleep();
throw new Exception(b);
});
}
catch (AggregateException ex)
{
foreach (var exception in ex.Flatten().InnerExceptions)
{
Console.WriteLine(exception.Message);
} }
ParallelOptions
看一下官方给出的解释:
public class ParallelOptions
{ public ParallelOptions(); //允许取消操作
public CancellationToken CancellationToken { get; set; } //允许指定可以并发操作的最大工作项目数
public int MaxDegreeOfParallelism { get; set; } //允许指定要使用哪个TaskScheduler
public TaskScheduler TaskScheduler { get; set; }
}
这个类提供我们最大并发数量的设置,取消操作的Token赋值,以及任务调度器的设置(关于这个,需要另拉一章来说,此处只关注前面两个)
//定义线程取消的一个对象
var cancel = new CancellationTokenSource(); var po = new ParallelOptions()
{
CancellationToken = cancel.Token,
MaxDegreeOfParallelism = 2,
};
//为了不堵塞线程,这里开启一个线程
Task.Run(() =>
{
try
{
Parallel.For(0, 1000, po, (i) =>
{
Thread.Sleep(1000);
po.CancellationToken.ThrowIfCancellationRequested();
Console.WriteLine(i);
});
}
catch (AggregateException ex)
{ foreach (var e in ex.Flatten().InnerExceptions)
{
Console.WriteLine(e.Message);
}
}
catch (Exception ex)
{
//OperationCanceledException
Console.WriteLine(ex.GetType().Name);
}
//不设置取消的TOKEN
}, CancellationToken.None); Thread.Sleep(10 * 1000);
cancel.Cancel();
线程最大并行数位2个,如果取消的话,整个Parallel会全部取消,并且抛出OperationCanceledException异常。
Parallel.For & Parallel.Foreach的重载
我只挑选了一个重载来说
public static ParallelLoopResult For<TLocal>
(int fromInclusive, int toExclusive,
Func<TLocal> localInit,
Func<int, ParallelLoopState, TLocal, TLocal> body,
Action<TLocal> localFinally);
来看一下官方的解释:
localInit:任务局部初始化,为参与工作的每一个任务都调用一次该委托这个委托在任务被要求处理一个工作项之前调用的。
boy:为参与工作的各个线程锁处理的每一项都调用一次该委托
localFinally:任务局部终结委托,为参与工作的每一个任务都调用一次该委托,这个委托是在任务处理好派给它的所有工作项之后调用的。即使主体委托代码引发一个未处理的异常,也会调用它。
public static void Parallel_For_Local_Test()
{
int[] nums = Enumerable.Range(, ).ToArray<int>();
long total = ;
ParallelLoopResult result = Parallel.For<long>(, nums.Length,
() => { return ; },
(j, loop, subtotal) =>
{
// 延长任务时间,更方便观察下面得出的结论
Thread.SpinWait();
Console.WriteLine("当前线程ID为:{0},j为{1},subtotal为:{2}。"
, Thread.CurrentThread.ManagedThreadId, j.ToString(), subtotal.ToString());
if (j == )
loop.Break();
if (j > loop.LowestBreakIteration)
{
Thread.Sleep();
Console.WriteLine("j为{0},等待4s种,用于判断已开启且大于阻断迭代是否会运行完。", j.ToString());
}
Console.WriteLine("j为{0},LowestBreakIteration为:{1}", j.ToString(), loop.LowestBreakIteration);
subtotal += nums[j];
return subtotal;
},
(finalResult) => Interlocked.Add(ref total, finalResult)
);
Console.WriteLine("total值为:{0}", total.ToString());
if (result.IsCompleted)
Console.WriteLine("循环执行完毕");
else
Console.WriteLine("{0}"
, result.LowestBreakIteration.HasValue ? "调用了Break()阻断循环." : "调用了Stop()终止循环.");
}
看一下输出
分析一下:
a) 泛型类型参数TLocal为本地线程数据类型,本示例设置为long。
b) 三个委托的参数解析body(j, loop, subtotal):首先初始委托localInit中返回了0,所以body委托中参数subtotal的初始值即为0,body委托的参数j对应的是当前迭代索引,参数loop为当前迭代状态ParallelLoopState对象;localFinally委托参数为body委托的返回值。
c) 三个委托三个阶段中都可能并行运行,因此您必须同步对任何共享变量的访问,如示例中在finally委托中使用了System.Threading.Interlocked对象。
d) 在索引为23的迭代中调用Break()后:
i. 索引小于23的所有迭代仍会运行(即使还未开始处理),并在退出循环之前处理完。
ii. 索引大于 23 的迭代若还未开启则会被放弃;若已处于运行中则会在退出循环之前处理完。
e) 对于调用Break()之后,在任何循环迭代中访问LowestBreakIteration属性都会返回调用Break()的迭代对应的索引。
ParallelLoopState
可用来使 Tasks.Parallel 循环的迭代与其他迭代交互,并为 Parallel 类的循环提供提前退出循环的功能。此类的实例不要自行创建,它由 Parallel 类创建并提供给每个循环项,并且只应该在提供此实例的“循环内部”使用。
public class ParallelLoopState
{
// 获取循环的任何迭代是否已引发相应迭代未处理的异常。
public bool IsExceptional { get; }
// 获取循环的任何迭代是否已调用 ParallelLoopState.Stop()。
public bool IsStopped { get; }
// 获取在Parallel循环中调用 ParallelLoopState.Break() 的最低循环迭代。
public long? LowestBreakIteration { get; }
// 获取循环的当前迭代是否应基于此迭代或其他迭代发出的请求退出。
public bool ShouldExitCurrentIteration { get; }
//通知Parallel循环当前迭代”之后”的其他迭代不需要运行。
public void Break();
//通知Parallel循环当前迭代“之外”的所有其他迭代不需要运行。
public void Stop();
}
Break()
Break()用于通知Parallel循环当前迭代“之后”的其他迭代不需要运行。例如,对于从 0 到 1000 并行迭代的 for 循环,如果在第 100 次迭代调用 Break(),则低于 100 的所有迭代仍会运行(即使还未开始处理),并在退出循环之前处理完。从 101 到 1000 中还未开启的迭代则会被放弃。
对于已经在执行的长时间运行迭代,Break()将为已运行还未结束的迭代对应ParallelLoopResult结构的LowestBreakIteration属性设置为调用Bread()迭代项的索引。
Stop()
Stop() 用于通知Parallel循环当前迭代“之外”的所有其他迭代不需要运行,无论它们是位于当前迭代的上方还是下方。
对于已经在执行的长时间运行迭代,可以检查 IsStopped属性,在观测到是 true 时提前退出。
Stop 通常在基于搜索的算法中使用,在找到一个结果之后就不需要执行其他任何迭代。(比如在看视频或漫画时自动匹配响应最快的服务器)
ShouldExitCurrentIteration 属性
当循环的迭代调用 Break 或 Stop时,或一个迭代引发异常,或取消循环时,Parallel 类将主动尝试禁止开始执行循环的其他迭代。但是,可能有无法阻止其他迭代启动的情况。也可能是长时间运行的迭代已经开始执行的情况。在此类情况下,迭代可以通过显式检查 ShouldExitCurrentIteration 属性,在该属性返回 true 时停止执行。
LowestBreakIteration 属性
返回过程中调用过Break方法的最低的项,如果从来没有调用过Break则返回null.
这里还有一个好玩的地方, 我们知道Parallel.For & Parallel.Foreach会返回一个ParallelLoopResult类型的对象,我们可以通过IsComplete()来判断是否结束并发,但是如果我们用了ParallelLoopState的Break(), 这个时候IsComplete()返回的是false,并且LowestBreakIteration属性不为null,如果调用的是Stop(), IsComplete()返回的是true,且LowestBreakIteration属性为null.
【C#】线程之Parallel的更多相关文章
- 并行编程多线程之Parallel
1.简介 随着多核时代的到来,并行开发越来越展示出它的强大威力!使用并行程序,充分的利用系统资源,提高程序的性能.在.net 4.0中,微软给我们提供了一个新的命名空间:System.Threadin ...
- C#多线程之Parallel中 类似于for的continue,break的方法
好久没写东西了,终于找到点知识记录下... 利用ParallelLoopState对象来控制Parallel.For函数的执行,ParallelLoopState对象是由运行时在后台创建的: Para ...
- 多线程之Parallel类
Parallel类是对线程的一个抽象.该类位于System.Threading.Tasks名称空间中,提供了数据和任务并行性. Paraller类定义了数据并行地For和ForEach的静态方法,以及 ...
- iOS多线程之8.NSOPeration的其他用法
本文主要对NSOPeration的一些重点属性和方法做出介绍,以便大家可以更好的使用NSOPeration. 1.添加依赖 - (void)addDependency:(NSOperation * ...
- python 线程之 threading(四)
python 线程之 threading(三) http://www.cnblogs.com/someoneHan/p/6213100.html中对Event做了简单的介绍. 但是如果线程打算一遍一遍 ...
- python 线程之 threading(三)
python 线程之 threading(一)http://www.cnblogs.com/someoneHan/p/6204640.html python 线程之 threading(二)http: ...
- python 线程之_thread
python 线程之_thread _thread module: 基本用法: def child(tid): print("hello from child",tid) _thr ...
- Java多线程之ConcurrentSkipListMap深入分析(转)
Java多线程之ConcurrentSkipListMap深入分析 一.前言 concurrentHashMap与ConcurrentSkipListMap性能测试 在4线程1.6万数据的条件下, ...
- iOS多线程之GCD小记
iOS多线程之GCD小记 iOS多线程方案简介 从各种资料中了解到,iOS中目前有4套多线程的方案,分别是下列4中: 1.Pthreads 这是一套可以在很多操作系统上通用的多线程API,是基于C语言 ...
随机推荐
- CSS层叠样式表的层叠是什么意思(转自知乎)
转自知乎上的回答:http://www.zhihu.com/question/20077745 解答一: 层叠指的是样式的优先级,当产生冲突时以优先级高的为准.1. 开发者样式>读者样式> ...
- EF OnModelCreating
http://www.cnblogs.com/libingql/p/3353112.html protected override void OnModelCreating(DbModel ...
- Asp.net Core WebApi 支持json/xml格式的数据返回
Asp.net core 在做webapi项目的时候,默认是只返回json格式的数据的,如果想要开启xml数据返回,需要在startup里配置如下: public void ConfigureServ ...
- android studio 翻译插件
插件下载地址 https://github.com/Skykai521/ECTranslation/releases 使用说明: http://gold.xitu.io/entry/573d8d92a ...
- [原]cocos2d-lua 常用法汇总
1.CCEditBox local back = CCScale9Sprite:create("res/ui/images/im_02.png", CCRect(20, 20, 1 ...
- Spring3系列12- Spring AOP AspectJ
Spring3系列12- Spring AOP AspectJ 本文讲述使用AspectJ框架实现Spring AOP. 再重复一下Spring AOP中的三个概念, Advice:向程序内部注入的代 ...
- 如何将 Microsoft Bot Framework 链接至微信公共号
说到 Microsoft Bot Framework 其实微软发布了已经有一段时间了,有很多朋友可能还不太了解,微软Bot的功能今天我给大家简单的介绍一下,Bot Framework的开发基础以及如何 ...
- Linux下MySQL不能远程访问
最近在Linux上装了个MySQL数据库,可是远程连接MySQL时总是报出erro 2003: Can't connect to MySQL server on '211.87.***.***' (1 ...
- 深入理解图优化与g2o:图优化篇
前言 本节我们将深入介绍视觉slam中的主流优化方法——图优化(graph-based optimization).下一节中,介绍一下非常流行的图优化库:g2o. 关于g2o,我13年写过一个文档,然 ...
- jdk的设置及安装android studio提示does not point to a valid jvm问题
设置方法: 我的电脑->属性->高级->环境变量->系统变量中添加以下环境变量: JAVA_HOME值为:安装JDK的目录, 我的为C:\Program Files\Java\ ...