C# Parallel之for,foreach使用(笔记)
C# 4.0 的新特性之并行库(TPL), 是 .NET Framework 4的 System.Threading 和 System.Threading.Tasks 命名空间中的一组公共类型和 API,基本包含以下几点:
- Parallel.For - for 循环的并行运算
- Parallel.ForEach - foreach 循环的并行运算
- Parallel.Invoke - 并行调用多个任务
- Task - 任务,基于线程池。其使我们对并行编程变得更简单,且不用关心底层是怎么实现的
- PLINQ - 用于对内存中的数据做并行运算,也就是说其只支持 LINQ to Object 的并行运算
今天先记录以下For,ForEach的基本使用,也是最常用到的。方便自己使用。
1.如果遍历集合里面的元素,做没有关系的处理时:
For:
public static class ParallelFor_narmal { // 每个之间独立,不需要交互 public static void narmal() { int[] arry = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; Parallel.For(0,arry.Length,(i,loopstate)=> { Console.Out.WriteLine(arry[i]); }); } }
ForEach的情况差不多,这种情况是最简单的。其实也就可以使用泛型集合的ForEach(Action(T))方法了.
2.如果集合里面的元素处理是有关联的,比如求和,但是并发是多线程的,按照传统的集合遍历肯定是有问题的,这个时候就需要了局部变量了。
For:
public static long withlocalvariable() { // int[] nums = Enumerable.Range(0, 1000000).ToArray(); int[] nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 }; long total = 0; object lockflg = new object(); // Use type parameter to make subtotal a long, not an int // 带有局部变量的并发。 // 第一个参数是for循环的起始迭代位置 // 第二个参数是for循环的终了迭代位置 // 第三个参数是局部变量的初始化。Func(T)。 这里就是给每个线程局部变量subtotal初始化为0; // 第四个参数就是每个线程的执行的方法体。原型为Func(int,ParallelLoopState,T,B),具有三个入力参数和一个返回值的代理。 // 其中j是运行时自动提供的迭代值,loop是用来控制循环的中断,subtotal就是局部变量,最有有一个返回值。 // 第五个参数为具有一个入力参数,没有返回值的代理Action(T),用来处理每个线程的返回的结果。因为是多线程处理,所有要注意线程安全 Parallel.For<long>(0, nums.Length, () => 0, (j, loop, subtotal) => { subtotal += nums[j]; return subtotal; }, (x) => { //Interlocked.Add(ref total, x); lock (lockflg) { total += x; } } ); return total; }
ForEach:
public static class ParallelForEach_WithLocalVariable { public static long withlocalvariable() { int[] nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 }; long total = 0; object lockflg = new object(); // ForEach<int, long> 第一个参数指定源元素的类型,此处的j,第二个参数指定线程本地变量的类型,此处的subtotal Parallel.ForEach<int, long>(nums, // 数据源 () => 0, // subtotal初始化 (j, loop, subtotal) =>{ subtotal +=j;return subtotal;}, // 每个线程执行的方法体 (x) =>{lock (lockflg) { total += x; }}); // 处理每个结果 return total; } }
这两种情况是最长使用到了,其实还有更细致的控制操作,有兴趣的可以看看 数据并行(任务并行库)