初识并行循环

并行循环主要用来处理数据并行的,如,同时对数组或列表中的多个数据执行相同的操作。

在C#编程中,我们使用并行类System.Threading.Tasks.Parallel提供的静态方法Parallel.ForParallel.ForEach来实现并行循环。从方法名可以看出,这两个方法是对常规循环forforeach的并行化。

简单用法

使用并行循环时需要传入循环范围(集合)和操作数据的委托Action<T>

Parallel.For(0, 100, i => { Console.WriteLine(i); });

Parallel.ForEach(Enumerable.Range(0, 100), i => { Console.WriteLine(i); });

使用场景

对于数据的处理需要耗费较长时间的循环适宜使用并行循环,利用多线程加快执行速度。

对于简单的迭代操作,且迭代范围较小,使用常规循环更好好,因为并行循环涉及到线程的创建、上下文切换和销毁,使用并行循环反而影响执行效率。

对于迭代操作简单但迭代范围很大的情况,我们可以对数据进行分区,再执行并行循环,减少线程数量。

循环结果

Parallel.ForParallel.ForEach方法的所有重载有着同样的返回值类型ParallelLoopResult,并行循环结果包含循环是否完成以及最低迭代次数两项信息。

下面的例子使用Parallel.ForEach展示了并行循环的结果。

ParallelLoopResult result = Parallel.ForEach(Enumerable.Range(0, 100), (i,loop) =>{    Console.WriteLine(i + 1);    Thread.Sleep(100);if (i == 30)    {        loop.Break();

    }});Console.WriteLine($"{result.IsCompleted}-{result.LowestBreakIteration}");

值得一提的是,循环的Break()Stop()只能尽早地跳出或者停止循环,而不能立即停止。

取消循环操作

有时候,我们需要在中途取消循环操作,但又不知道确切条件是什么,比如用户触发的取消。这时候,可以利用循环的ParallelOptions传入一个CancellationToken,同时使用异常处理捕获OperationCanceledException以进行取消后的处理。下面是一个简单的例子。



public static CancellationTokenSource CTSource { get; set; } = new CancellationTokenSource();

public static void CancelParallelLoop(){    Task.Factory.StartNew(() =>    {try        {            Parallel.ForEach(Enumerable.Range(0, 100), new ParallelOptions { CancellationToken = CTSource.Token },                i =>                {                    Console.WriteLine(i + 1);                    Thread.Sleep(1000);                });        }catch (OperationCanceledException oce)        {            Console.WriteLine(oce.Message);        }    });}
static void Main(string[] args){    ParallelDemo.CancelParallelLoop();    Thread.Sleep(3000);    ParallelDemo.CTSource.Cancel();

    Console.ReadKey();}

循环异常收集

并行循环执行过程中,可以捕获并收集迭代操作引发的异常,循环结束时抛出一个AggregateException异常,并将收集到的异常赋给它的内部异常集合InnerExceptions。外部使用时,捕获AggregateException,即可进行并行循环的异常处理。

下面的例子模拟了并行循环的异常抛出、收集及处理的过程。



public static void CaptureTheLoopExceptions(){    ConcurrentQueue<Exception> exceptions = new ConcurrentQueue<Exception>();    Parallel.ForEach(Enumerable.Range(0, 100), i =>    {try        {if (i % 10 == 0)            {throw new Exception($"{DateTime.Now}=> Thread-[{Thread.CurrentThread.ManagedThreadId}] had thrown a exception. [{i}]");            }            Console.WriteLine(i + 1);            Thread.Sleep(100);        }catch (Exception ex)        {            exceptions.Enqueue(ex);        }    });

if (!exceptions.IsEmpty)    {throw new AggregateException(exceptions);    }}

外部处理方式

try{    ParallelDemo.CaptureTheLoopExceptions();}catch (AggregateException aex){foreach (Exception ex in aex.InnerExceptions)    {        Console.WriteLine(ex.Message);    }}

分区并行处理

当循环操作很简单,迭代范围很大的时候,ParallelLoop提供一种分区的方式来优化循环性能。下面的例子展示了分区循环的使用,同时也能比较几种循环方式的执行效率。



public static void PartationParallelLoop(int rangeSize = 10000, int opDuration = 1){

    Stopwatch watch0 = Stopwatch.StartNew();    Parallel.ForEach(Partitioner.Create(Enumerable.Range(0, rangeSize), EnumerablePartitionerOptions.None),        i =>        {            Console.WriteLine($"{DateTime.Now}=> Thread-[{Thread.CurrentThread.ManagedThreadId}] was running. [{i}]");            Thread.Sleep(opDuration);        });    watch0.Stop();

    Stopwatch watch1 = Stopwatch.StartNew();    Parallel.ForEach(Partitioner.Create(Enumerable.Range(0, rangeSize),EnumerablePartitionerOptions.NoBuffering),        i =>        {            Console.WriteLine($"{DateTime.Now}=> Thread-[{Thread.CurrentThread.ManagedThreadId}] was running. [{i}]");            Thread.Sleep(opDuration);        });    watch1.Stop();

    Stopwatch watch2 = Stopwatch.StartNew();    Parallel.ForEach(Enumerable.Range(0, rangeSize),        i =>        {            Console.WriteLine($"{DateTime.Now}=> Thread-[{Thread.CurrentThread.ManagedThreadId}] was running. [{i}]");            Thread.Sleep(opDuration);        });    watch2.Stop();

    Stopwatch watch3 = Stopwatch.StartNew();foreach (int i in Enumerable.Range(0, rangeSize))    {        Console.WriteLine($"{DateTime.Now}=> Thread-[{Thread.CurrentThread.ManagedThreadId}] was running. [{i}]");        Thread.Sleep(opDuration);    }    watch2.Stop();

    Console.WriteLine();    Console.WriteLine($"PartationParallelLoopWithBuffer    => {watch0.ElapsedMilliseconds}ms");    Console.WriteLine($"PartationParallelLoopWithoutBuffer => {watch1.ElapsedMilliseconds}ms");    Console.WriteLine($"NormalParallelLoop                 => {watch2.ElapsedMilliseconds}ms");    Console.WriteLine($"NormalLoop                         => {watch3.ElapsedMilliseconds}ms");}

在 I7-7700HQ + 16GB 配置 VS调试模式下得到下面一组测试结果。

10000,1 10527 11799 11155 19434
10000,1 9513 11442 11048 19354
10000,1 9871 11391 14782 19154
100,1000 9107 5951 5081 100363
100,1000 9086 5974 5187 100162
100,1000 9208 5125 5255 100239
100,1 350 439 243 200
100,1 390 227 166 198
100,1 466 225 84 197

应该根据不同的应用场景选择合适的循环策略,具体如何选择,朋友们可自行体会~

原文地址:https://www.cnblogs.com/chenbaoshun/p/10572639.html

.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com 

C#并行编程(3):并行循环相关推荐

  1. Java并行编程–从并行任务集获取反馈

    Java并行编程–从并行任务集获取反馈 在并行任务启动后,强制性地从并行任务得到反馈. 假想有一个程序,可以发送批邮件,还使用了多线程机制.你想知道有多少邮件成功发送吗?你想知道在实际发送过程期间,这 ...

  2. python多线程并行编程_Python并行编程(二):基于线程的并行

    1.介绍 软件应用中使用最广泛的并行编程范例是多线程.通常一个应用有一个进程,分成多个独立的线程,并行运行.互相配合,执行不同类型的任务. 线程是独立的处理流程,可以和系统的其他线程并行或并发地执行. ...

  3. python多线程并行编程,Python并行编程(二):基于线程的并行

    1.介绍 软件应用中使用最广泛的并行编程范例是多线程.通常一个应用有一个进程,分成多个独立的线程,并行运行.互相配合,执行不同类型的任务. 线程是独立的处理流程,可以和系统的其他线程并行或并发地执行. ...

  4. C#并行编程中的Parallel.Invoke

    一.基础知识 并行编程:并行编程是指软件开发的代码,它能在同一时间执行多个计算任务,提高执行效率和性能一种编程方式,属于多线程编程范畴.所以我们在设计过程中一般会将很多任务划分成若干个互相独立子任务, ...

  5. C#并发编程之初识并行编程

    写在前面 之前微信公众号里有一位叫sara的朋友建议我写一下Parallel的相关内容,因为手中商城的重构工作量较大,一时之间无法抽出时间.近日,这套系统已有阶段性成果,所以准备写一下Parallel ...

  6. .Net并行编程系列文章导航

    .Net4.0并行编程系列文章如下: 多核时代 .NET Framework 4 中的并行编程9---线程安全集合类 多核时代 .NET Framework 4 中的并行编程8---任务的同步 多核时 ...

  7. .NET并行编程实践(一:.NET并行计算基本介绍、并行循环使用模式)

    阅读目录: 1.开篇介绍 2.NET并行计算基本介绍 3.并行循环使用模式 3.1并行For循环 3.2并行ForEach循环 3.3并行LINQ(PLINQ) 1]开篇介绍 最近这几天在捣鼓并行计算 ...

  8. dnet 并行编程学习总结

    .Net并行编程高级教程--Parallel http://www.cnblogs.com/stoneniqiu/p/4857021.html 一直觉得自己对并发了解不够深入,特别是看了<代码整 ...

  9. 并行编程中的“锁”难题

    在并行程序中,锁的使用会主要会引发两类难题:一类是诸如死锁.活锁等引起的多线程Bug:另一类是由锁竞争引起的性能瓶颈.本文将介绍并行编程中因为锁引发的这两类难题及其解决方案. 1. 用锁来防止数据竞跑 ...

  10. python并行编程语言_Python3 系列之 并行编程

    Python Python开发 Python语言 Python3 系列之 并行编程 进程和线程 进程是程序运行的实例.一个进程里面可以包含多个线程,因此同一进程下的多个线程之间可以共享线程内的所有资源 ...

最新文章

  1. fail2ban防止暴力破解
  2. oracle /+append/好用吗,ORACLE-insert /*+append*/提高性能
  3. 【Silverlight】汉诺塔游戏,带AI
  4. sqlmap的使用 ---- 自带绕过脚本tamper
  5. 使用备用访问映射改变站点访问路径
  6. 华为s8600手机驱动_只有手机才能快充?华为MateBook X的灵巧快充解放你的续航焦虑-华为 ——快科技(驱动之家旗下媒体)-...
  7. 干货|训练神经网络时要知道的几个要点
  8. 原创内容将成网络视频的一支奇军
  9. RegistryBoostry2010/2011/2012的破解方法
  10. csharp:Compare two DataTables to rows in one but not the other
  11. 十进制转换为任意进制及操作符重载
  12. Atitiit java通过Exchange协议同步note 记事本 目录 1.1.1. 使用EWS(Exchange Web Service)协议读取邮件、发送邮件 1 最新问题 1 热门问题 1
  13. 快速开发微信小程序直播--微信直播--小程序直播开发
  14. java大赛参赛学院名单,河南大学第十一届“学佳澳杯”大学生程序设计竞赛获奖名单公布...
  15. ES3之cookie
  16. php获取alexa世界排名值的函数
  17. 正确划分音节——英语见字发音的秘诀
  18. SVN update拒绝访问,clean up失败
  19. 撰写 SCI 论文时,有什么好用的软件或者技巧吗?
  20. 笔记本锁定计算机怎么解锁,笔记本键盘被锁定了怎么办 笔记本解锁键盘的方法...

热门文章

  1. craigslist_如何设置Craigslist警报(用于电子邮件或SMS)
  2. unity 使用tile_如何使用Tile从网上查找电话
  3. std::bind 详解及参数解析
  4. MySQL备份原理详解
  5. maven3安装和使用笔记
  6. 52Exchange 2010升级到Exchange 2013-升级SH站点Ex2010到2013
  7. 堪比JMeter的.Net压测工具 - Crank 入门篇
  8. 重要更新,Office Add-in将全面支持Webview2
  9. Windows 11 小技巧- WSL开启Linux桌面应用
  10. UOS LoongArch 上成功安装.NET Core 3.1