在一些常见的编程情形中,使用任务也许能提升性能。为了简化变成,静态类System.Threading.Tasks.Parallel封装了这些常见的情形,它内部使用Task对象。

Parallel.For & Parallel.Foreach & Pararllel.Invoke 

           Parallel.For(0, 1000, (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(1000);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; }//允许指定要使用哪个TaskSchedulerpublic 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(0, 1000000).ToArray<int>();long total = 0;ParallelLoopResult result = Parallel.For<long>(0, nums.Length,() => { return 0; },(j, loop, subtotal) =>{// 延长任务时间,更方便观察下面得出的结论
Thread.SpinWait(200);Console.WriteLine("当前线程ID为:{0},j为{1},subtotal为:{2}。", Thread.CurrentThread.ManagedThreadId, j.ToString(), subtotal.ToString());if (j == 23)loop.Break();if (j > loop.LowestBreakIteration){Thread.Sleep(4000);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("循环执行完毕");elseConsole.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.

转载于:https://www.cnblogs.com/guochenkai/p/3987968.html

【C#】线程之Parallel相关推荐

  1. 抽象线程之Parallel类

    该类定义了并行的For和Foreach方法,Parallel类使用多个任务,因此需要多个线程来完成这个作业 代码及简单注释如下: using System; using System.Collecti ...

  2. 异步多线程之Parallel详解

    上一篇:异步多线程之入Task 下一篇:异步多线程使用中的常见问题 简介 Parallel 叫做并行编程 .Net 4.5 时代的,基于 Task 基础上做了封装.Parallel 的特点方便控制线程 ...

  3. linux 线程pthread_detach,linux线程之pthread_join和pthread_detach

    在任何一个时间点上,线程是可结合的(joinable)或者是分离的(detached).一个可结合的线程能够被其他线程收回其资源和杀死.在 被其他线程回收之前,它的存储器资源(例如栈)是不释放的.相反 ...

  4. 多线程之CompletableFuture和ParallelStream应该使用哪个?

    多线程之CompletableFuture和ParallelStream应该使用哪个 简介 实例场景 测试代码 开始测试 总结 备注 简介 随着用户对性能和体验的要求越来越高,异步在项目中用的也就越来 ...

  5. Android多线程之ArrayBlockingQueue源码解析

    阻塞队列系列 Android多线程之LinkedBlockingQueue源码解析 Android多线程之SynchronousQueue源码解析 Andorid多线程之DelayQueue源码分析 ...

  6. Asp.Net Core 轻松学-多线程之Task快速上手

    Asp.Net Core 轻松学-多线程之Task快速上手 原文:Asp.Net Core 轻松学-多线程之Task快速上手 前言     Task是从 .NET Framework 4 开始引入的一 ...

  7. JAVA多线程之wait/notify

    本文主要学习JAVA多线程中的 wait()方法 与 notify()/notifyAll()方法的用法. ①wait() 与 notify/notifyAll 方法必须在同步代码块中使用 ②wait ...

  8. Java多线程之Callable、Future和FutureTask

    Java多线程之Callable接口 自己想总结一下的,看到一篇总结的更好的博客,就转载了,突然感觉真轻松,哈哈哈哈 文章转载于:Matrix海子:Java并发编程:Callable.Future和F ...

  9. Java多线程之Synchronized和Lock的区别

    Java多线程之Synchronized和Lock的区别 目录: 原始构成 使用方法 等待是否可以中断 加锁是否公平 锁绑定多个条件Condition 小结:Lock相比较Synchronized的优 ...

最新文章

  1. 我的世界java版forge放在哪_我的世界1.9forge安装 怎么装forge教程
  2. SQLite 性能优化
  3. 【Android】Android中WIFI开发总结(一)
  4. Firefox for iOS现身Github 使用Swift编写
  5. 用Asp.net写自己的服务框架
  6. 数据库索引与视图实验
  7. Word去除段前黑点
  8. 自动控制原理3.3---二阶系统的时域分析
  9. 计算机专业就业方向 【转】
  10. html5 电子白板 直播,基于HTML5技术的智能终端电子白板软件的设计与实现
  11. 软件测试需求频繁变更,软件测试人员如何测试需求频繁变动的项目
  12. 与小卡特一起学python 豆瓣_《父与子的编程之旅:与小卡特一起学Python》
  13. 一键检测Linux VPS/服务器配置、IO、国内节点下载测速
  14. 微信小程序云开发获取上传图片后https的url链接地址
  15. python如何赋值给元组_【Python 1-9】Python手把手教程之——元组和元组的使用技巧...
  16. 循序渐进学 LoadingDrawable
  17. 手机电脑卖不动,AR\VR和自动驾驶也不给力;苹果的未来在哪里?
  18. 可视化网站教程:最流行的搭建工具及必学的7个功能
  19. Linux FQ 队列实现原理浅析
  20. 用Cheat Engine无限期体验百度云盘会员提速

热门文章

  1. lisp 吴永进_采用Visual Lisp软件提高长输管道施工图设计效率
  2. 阿里云服务器怎么样可以实现 frp 内网穿透
  3. 一、万维网的发展(W3C组织的建立)
  4. 爬取manhua.fzdm.com上的妖尾漫画
  5. 收藏!2020 年最具潜力 44 个顶级开源项目,涵盖 11 类 AI 学习框架、平台
  6. python变量名必须以字母或下划线开头不区分字母大小写_Python 变量名必须以字符或下划线开头,并且区分字母大小写。_学小易找答案...
  7. 数据分析Qgis-城市餐饮店铺选址
  8. Pandas数据视图 — groupby 和 pivot
  9. 蚂蚁金服开源-SofaRpc源码学习篇
  10. python使用大漠插件教程_python调用大漠插件教程05字库