在一些常见的编程情形中,使用任务也许能提升性能。为了简化变成,静态类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的更多相关文章

  1. 并行编程多线程之Parallel

    1.简介 随着多核时代的到来,并行开发越来越展示出它的强大威力!使用并行程序,充分的利用系统资源,提高程序的性能.在.net 4.0中,微软给我们提供了一个新的命名空间:System.Threadin ...

  2. C#多线程之Parallel中 类似于for的continue,break的方法

    好久没写东西了,终于找到点知识记录下... 利用ParallelLoopState对象来控制Parallel.For函数的执行,ParallelLoopState对象是由运行时在后台创建的: Para ...

  3. 多线程之Parallel类

    Parallel类是对线程的一个抽象.该类位于System.Threading.Tasks名称空间中,提供了数据和任务并行性. Paraller类定义了数据并行地For和ForEach的静态方法,以及 ...

  4. iOS多线程之8.NSOPeration的其他用法

      本文主要对NSOPeration的一些重点属性和方法做出介绍,以便大家可以更好的使用NSOPeration. 1.添加依赖 - (void)addDependency:(NSOperation * ...

  5. python 线程之 threading(四)

    python 线程之 threading(三) http://www.cnblogs.com/someoneHan/p/6213100.html中对Event做了简单的介绍. 但是如果线程打算一遍一遍 ...

  6. python 线程之 threading(三)

    python 线程之 threading(一)http://www.cnblogs.com/someoneHan/p/6204640.html python 线程之 threading(二)http: ...

  7. python 线程之_thread

    python 线程之_thread _thread module: 基本用法: def child(tid): print("hello from child",tid) _thr ...

  8. Java多线程之ConcurrentSkipListMap深入分析(转)

    Java多线程之ConcurrentSkipListMap深入分析   一.前言 concurrentHashMap与ConcurrentSkipListMap性能测试 在4线程1.6万数据的条件下, ...

  9. iOS多线程之GCD小记

    iOS多线程之GCD小记 iOS多线程方案简介 从各种资料中了解到,iOS中目前有4套多线程的方案,分别是下列4中: 1.Pthreads 这是一套可以在很多操作系统上通用的多线程API,是基于C语言 ...

随机推荐

  1. 转:Android开发之旅:环境搭建及HelloWorld

    http://www.cnblogs.com/skynet/archive/2010/04/12/1709892.html 引言 本系列适合0基础的人员,因为我就是从0开始的,此系列记录我步入Andr ...

  2. 出现Assertion failure in -[***** layoutSublayersOfLayer:]

    在自定义的view中使用了[self layoutIfNeeded]方法,在iOS8\9上都没有错误,但是在iOS7上出现了页面错乱,解决方案就是在自定义的view里面添加如下代码: + (void) ...

  3. iOS工程集成支付宝错误Undefined symbols for architecture armv7

    问题描述: 新工程中需要集成支付宝功能,于是咱就把支付宝的库给集成了进入然后就出现了下面这种错误了说,错误信息如下: Undefined symbols for architecture armv7: ...

  4. Request is not available in this context

    部署到新服务器的IIS的时候发现这个错误: Request is not available in this context 解决方案: <system.web> <customEr ...

  5. PLSQL登录弹出空白框如何解决

     转自:http://jingyan.baidu.com/article/066074d6760959c3c21cb0d6.html   出现登录弹出空白框这是由于win7的安全性提高了,在PLSQL ...

  6. saiku 元数据存储分析

    一.介绍 使用saiku的人一定对他的元数据存储都特别感兴趣,特别是有分布式管理需求的项目,更是迫切需要了解.其实它是使用Apache的开源项目Jackrabbit管理文件的! 二.代码跟踪 我也是使 ...

  7. python string.py 源码分析 三:maketrans

    l = map(chr, xrange(256)) #将ascii转为字符串 _idmap = str('').join(l) del l # Construct a translation stri ...

  8. 创建 iPhone/iOS8 弹出菜单(窗口)

    基本步骤 添加视图:主视图与弹出视图 关联视图 配置弹出视图 编码实现:弹出菜单样式及控制器委托 override func prepareForSegue(segue: UIStoryboardSe ...

  9. 路由器换大Flash

    使用winhex自建编程器固件(我的是TP-WR941N V6) 1:使用winhex新建一个8M,16M的文件,编辑-全选,填充选块,填充十六进制数值 FF : 2:打开4M的原厂编程器固件(或者自 ...

  10. 如何克隆kvm虚拟机

    关于如何使用kvm虚拟化技术创建虚拟机,这里有一系列博客讲的已经非常清楚了,这里不再赘述,不过其中有些小坑可能需要大家注意: 0. 写在创建虚拟机之前(即教程的系列三之前) 1. 确认防火墙是否关闭, ...