引言

随着CPU多核的普及,编程时充分利用这个特性越显重要。上篇首先用传统的嵌套循环进行数组填充,然后用.NET 4.0中的System.Threading.Tasks提供的Parallel Class来并行地进行填充,最后对比他们的性能。本文将深入分析Parallel Class并借机回答上篇9楼提出的问题,而System.Threading.Tasks分析,这个将推迟到.NET(C#) Internals: 以一个数组填充的例子初步了解.NET 4.0中的并行(三)中介绍。内容如下:

  • 1、Parallel Class

    • 1.1、For方法
    • 1.2、ForEach方法
    • 1.3、Invoke方法
  • 2、并发控制疑问?
    • 2.1、使用Lock锁
    • 2.2、使用PLINQ——用AsParallel
    • 2.3、使用PLINQ——用ParallelEnumerable
    • 2.4、使用Interlocked操作
    • 2.5、使用Parallel.For的有Thread-Local变量重载函数
  • 性能比较

1、Parallel Class

Parallel——这个类提供对通常操作(诸如for、foreach、执行语句块)基于库的数据并行替换。它只是System.Threading.Tasks命名空间的一个类,该命名空间中还包括很多其他的类。下面举个例子来说明如何使用Parallel.For(来自MSDN):

view source print?
01 using System.Threading.Tasks;   
02 class Test
03 {
04     static int N = 1000;
05   
06     static void TestMethod()
07     {
08         // Using a named method.
09         Parallel.For(0, N, Method2);
10   
11         // Using an anonymous method.
12         Parallel.For(0, N, delegate(int i)
13         {
14             // Do Work.
15         });
16   
17         // Using a lambda expression.
18         Parallel.For(0, N, i =>
19         {
20             // Do Work.
21         });
22     }
23   
24     static void Method2(int i)
25     {
26         // Do work.
27     }
28 }

上面这个例子简单易懂,上篇我们就是用的Parallel.For,这里就不解释了。其实Parallel类的方法主要分为下面三类:

  • For方法
  • ForEach方法
  • Invoke方法

1.1、For方法

在里面执行的for循环可能并行地运行,它有12个重载。这12个重载中Int32参数和Int64参数的方法各为6个,下面以Int32为例列出:

  • For(Int32 fromInclusive, Int32 toExclusive, Action<Int32> body),该方法对区间(fromInclusive,toExclusive)之间的迭代调用body表示的委托。body委托有一个迭代数次的int32参数,如果fromInclusive>=toExclusive,则不会执行任何迭代。
  • For(Int32 fromInclusive, Int32 toExclusive, Action<Int32, ParallelLoopState>),该方法对区间(fromInclusive, toExclusive)之间的迭代调用body表示的委托。body委托有两个参数——表示迭代数次的int32参数、一个可用于过早地跳出循环的ParallelLoopState实例。如果fromInclusive>=toExclusive,则不会执行任何迭代。
    调用Break通知For操作当前迭代之后的迭代不需要执行。然而,在此之前的迭代如果没有完成仍然需要执行。因此,调用Break类似于调用break跳出传统的for循环,不是break的原因是它不保证当前迭代之后的迭代绝对不会执行。
    如果在当前迭代之前的迭代不必要执行,应该调用Stop而不是Break。调用Stop通知For循环放弃剩下的迭代,不管它是否在当前迭代之前或之后,因为所以要求的工作已经完成。然而,Break并不能保证这个。
  • For(Int32 fromInclusive, Int32 toExclusive, ParallelOptions parallelOptions, Action<Int32> body),跟第一个方法类似,但它的区间是[fromInclusive, toExclusive)。
  • For(Int32 fromInclusive, Int32 toExclusive, ParallelOptions parallelOptions, Action<Int32, ParallelLoopState> body),跟第二个方法类似,单的区间是[fromInclusive, toExclusive)。
  • For<TLocal>(Int32 fromInclusive, Int32 toExclusive, Func<TLocal> localInit, Func<Int32, ParallelLoopState, TLocal, TLocal> body, Action<TLocal> localFinally),它的迭代区间是[fromInclusive, toExclusive)。另外body有两个local状态变量用于同一线程的迭代之间共享。localInit委托将在每个线程参与循环执行时调用,并返回这些线程初始的local状态。这些初始状态被传递给body,当它在每个线程上第一次调用时。然后,接下来body调用返回一个可能的修改状态值且传递给下一次body调用。最终,最后一次在每个线程上的body调用返回的一个状态值传递给localFinally委托。每个线程执行在自己的loacl 状态上执行最后一个动作时,localFinally委托将被调用。这个委托可能在多个线程上并发执行,因此,你必须同步访问任何共享变量。
  • For<TLocal>(Int32, Int32, ParallelOptions, Func<TLocal>, Func<Int32, ParallelLoopState, TLocal, TLocal>, Action<TLocal>),跟上面的方法类似。

下面代码演示了For(Int32 fromInclusive, Int32 toExclusive, ParallelOptions parallelOptions, Action<Int32> body)方法(来自MSDN):

show sourceview source print?
01 using System;
02 using System.Collections.Generic;
03 using System.Linq;
04 using System.Text;
05 using System.Threading;
06 using System.Threading.Tasks;
07   
08 namespace ConsoleApplication2
09 {
10     class Program
11     {
12         // Demonstrated features:
13         //        CancellationTokenSource
14         //         Parallel.For()
15         //        ParallelOptions
16         //        ParallelLoopResult
17         // Expected results:
18         //         An iteration for each argument value (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) is executed.
19         //        The order of execution of the iterations is undefined.
20         //        The iteration when i=2 cancels the loop.
21         //        Some iterations may bail out or not start at all; because they are temporally executed in unpredictable order, 
22         //          it is impossible to say which will start/complete and which won't.
23         //        At the end, an OperationCancelledException is surfaced.
24         // Documentation:
25         //        http://msdn.microsoft.com/en-us/library/system.threading.cancellationtokensource(VS.100).aspx
26   
27         static void Main(string[] args)
28         {
29             CancellationTokenSource cancellationSource = new CancellationTokenSource();
30             ParallelOptions options = new ParallelOptions();
31             options.CancellationToken = cancellationSource.Token;
32             try
33             {
34                 ParallelLoopResult loopResult = Parallel.For(
35                     0,
36                     10,
37                     options,
38                     (i, loopState) =>
39                     {
40                         Console.WriteLine("Start Thread={0}, i={1}", Thread.CurrentThread.ManagedThreadId, i);
41   
42                         // Simulate a cancellation of the loop when i=2
43                         if (i == 2)
44                         {
45                             cancellationSource.Cancel();
46                         }
47   
48                         // Simulates a long execution
49                         for (int j = 0; j < 10; j++)
50                         {
51                             Thread.Sleep(1 * 200);
52   
53                             // check to see whether or not to continue
54                             if (loopState.ShouldExitCurrentIteration) return;
55                         }
56   
57                         Console.WriteLine("Finish Thread={0}, i={1}", Thread.CurrentThread.ManagedThreadId, i);
58                     }
59                 );
60                 if (loopResult.IsCompleted)
61                 {
62                     Console.WriteLine("All iterations completed successfully. THIS WAS NOT EXPECTED.");
63                 }
64             }
65                 // No exception is expected in this example, but if one is still thrown from a task,
66                 // it will be wrapped in AggregateException and propagated to the main thread.
67             catch (AggregateException e)
68             {
69                 Console.WriteLine("Parallel.For has thrown an AggregateException. THIS WAS NOT EXPECTED.\n{0}", e);
70             }
71                 // Catching the cancellation exception
72             catch (OperationCanceledException e)
73             {
74                 Console.WriteLine("An iteration has triggered a cancellation. THIS WAS EXPECTED.\n{0}", e.ToString());
75             }
76         }
77     }
78 }

1.2、ForEach方法

在迭代中执行的foreach操作可能并行地执行,它有20个重载。这个方法太多,但用法大概跟For方法差不多,请自行参考MSDN。

1.3、Invoke方法

提供的每个动作可能并行地执行,它有2个重载。

  • Invoke(params Action[] actions):actions是一个要执行的动作数组,这些动作可能并行地执行,但并不保证执行的顺序及一定并行执行。这个方法直到提供的所有操作完成时才返回,不管是否正常地完成或异常终止。
  • Invoke(ParallelOptions parallelOptions, params Action[] actions):跟上面的方法类似,只是增加了一个parallelOptions参数,可以用户调用者取消整个操作。

例如下面代码执行了三个操作(来自MSDN):

show sourceview source print?
01 using System;
02 using System.Collections.Generic;
03 using System.Linq;
04 using System.Text;
05 using System.Threading;
06 using System.Threading.Tasks;
07   
08 namespace ConsoleApplication2
09 {
10     class Program
11     {
12         static void Main()
13         {
14             try
15             {
16                 Parallel.Invoke(
17                     BasicAction,    // Param #0 - static method
18                     () =>            // Param #1 - lambda expression
19                     {
20                         Console.WriteLine("Method=beta, Thread={0}", Thread.CurrentThread.ManagedThreadId);
21                     },
22                     delegate()        // Param #2 - in-line delegate
23                     {
24                         Console.WriteLine("Method=gamma, Thread={0}", Thread.CurrentThread.ManagedThreadId);
25                     }
26                 );
27             }
28             // No exception is expected in this example, but if one is still thrown from a task,
29             // it will be wrapped in AggregateException and propagated to the main thread.
30             catch (AggregateException e)
31             {
32                 Console.WriteLine("An action has thrown an exception. THIS WAS UNEXPECTED.\n{0}", e.InnerException.ToString());
33             }
34         }
35   
36         static void BasicAction()
37         {
38             Console.WriteLine("Method=alpha, Thread={0}", Thread.CurrentThread.ManagedThreadId);
39         }
40     }
41 }

2、并发控制疑问?

有人提出以下疑问:“如果For里面的东西,对于顺序敏感的话,会不会有问题。并行处理的话,说到底应该是多线程。如果需要Lock住什么东西的话,应该怎么做呢?例如这个例子不是对数组填充,是对文件操作呢?对某个资源操作呢?”

关于对顺序敏感的话,也就是说该如何加锁来控制?下面我举个例子来说明:对1~1000求和。如果我们想上篇那样简单地用Parallel.For,将会产生错误的结果,代码如下:

view source print?
01 using System;
02 using System.Collections.Generic;
03 using System.Linq;
04 using System.Text;
05 using System.Threading;
06 using System.Threading.Tasks;
07   
08 namespace ConsoleApplication2
09 {
10     class Program
11     {        
12         static void Main(string[] args)
13         {
14             int loops=0;
15             while (loops <= 100)
16             {
17                 long sum = 0;                
18                 Parallel.For(1, 1001, delegate(long i)
19                 {
20                     sum += i;
21                 });
22                 System.Console.WriteLine(sum);
23                 loops++;
24             }
25         }
26     }
27 }

在上述代码中,为了校验正确性我进行了重复做了100次,得出如下结果:

Net 4.0并行库实用性演练相关推荐

  1. .Net 4.0并行库实用性演练[1]

    自VS2010发布近半年了,虽然整天想学习新东西,要更新到自己时,发现原来自己基本也很懒,2008还没用上多久呢,无奈被2010了.用了几天,IDE模样还是和05.08差不多,加了些小特性,以后慢慢体 ...

  2. .Net 4.0并行库实用性演练

    前面说在练习Parallel时,发现另有乾坤,是这样的代码: 代码 static IEnumerable<Person> testFill() { var list =new List&l ...

  3. .Net4.0并行库介绍——Cancellation Framework

    在.net 4.0中,引入了一个新的类CancellationToken,这个类基本上集成了我们各种常用的取消方式,在并发任务中非常有用. 同步模式下的取消: 一种比较常见的需要支持取消功能的的是一些 ...

  4. .NET4.0并行计算技术基础(8)

    说明: 要想看懂本系列文章,需要您对.NET多线程开发有基本的了解.我在新书<面向对象的艺术 --.NET Framework 4.0技术剖析与应用>(暂名)中花了近200页的篇幅来介绍. ...

  5. 多线程编程学习笔记——任务并行库(二)

    接上文 多线程编程学习笔记--任务并行库(一) 三.   组合任务 本示例是学习如何设置相互依赖的任务.我们学习如何创建一个任务的子任务,这个子任务必须在父任务执行结束之后,再执行. 1,示例代码如下 ...

  6. 多线程编程学习笔记——任务并行库(三)

    接上文 多线程编程学习笔记--任务并行库(一) 接上文 多线程编程学习笔记--任务并行库(二) 六.   实现取消选项 本示例学习如何实现基于Task的异步操作进行取消流程,以及在任务真正运行前如何知 ...

  7. C#多线程开发-任务并行库

    你好,我是阿辉. 正文共2090字,预计阅读时间:6min. 之前学习了线程池,知道了它有很多好处. 使用线程池可以使我们在减少并行度花销时节省操作系统资源.可认为线程池是一个抽象层,其向程序员隐藏了 ...

  8. 三分钟总览微软任务并行库TPL

    点击上方蓝字进行关注 有小伙伴问我每天忽悠的TPL是什么? ☹️ 这次站位高一点,严肃讲一讲. 引言 俗话说,不想开飞机的程序员不是一名好爸爸:作为微软技术栈的老鸟,一直将代码整洁之道奉为经典, 优秀 ...

  9. .NET异步程序设计之任务并行库

    目录 1.简介 2.Parallel类 2.0 Parallel类简介 2.1 Parallel.For() 2.2 Parallel.ForEach() 2.3 Parallel.Invoke() ...

最新文章

  1. 在Linux上编写并运行Python文件
  2. css背景图根据屏幕大小自动缩放
  3. docker显示镜像的摘要信息
  4. shell循环结构之while循环
  5. 利用Python进行数据分析--数据加载、存储与文件格式
  6. innerHTML和outerHTML有什么区别
  7. idea配置Idea类注释模板和方法注释模板(亲测有效)
  8. 【贪心+堆优化】奶牛晒衣服 dry.pas/c/cpp
  9. 二级域名使用https证书注意事项
  10. C++入门基础知识总结(2022整理)
  11. mysql数据库连接报错:is not allowed to connect to this mysql server
  12. 手机中的RAM和ROM分别对应电脑的内存和硬盘
  13. python批量改变图像大小
  14. 巨杉数据库全新认证机制来袭!首期考试时间公布
  15. 【测试开发】一文带你了解什么是软件测试
  16. BES(恒玄) 平台 复杂按键 实现
  17. JAVA网络爬爬学习之HttpClient+Jsoup
  18. chrome电脑棒 刷linux,其实已经够用:华硕Chromebit电脑棒评测
  19. web 移动端 微信分享(nativeShare)
  20. HTML+CSS制作二级菜单栏

热门文章

  1. 信息学奥赛一本通(1241:二分法求函数的零点)
  2. 信息学奥赛一本通(2044:【例5.12】回文字串)
  3. 暑期训练日志----2018.7.31
  4. 移动路线(信息学奥赛一本通-T1194)
  5. 火柴棒等式(洛谷-P1149)
  6. 31 WM配置-策略-出库策略4-定义部分货架数量策略A(Partial Pallet Quantity)
  7. python规定浮点数类型可以不带小数部分吗_Python标准数据类型-数字
  8. python随机生成列表_python 实现快速生成连续、随机字母列表
  9. 16进制 hbase phoenix_HBase 和 Phoenix 的使用
  10. mysql set substring_MySQL substring()函数