C#并行编程:Parallel类

说明:

Parallel类中提供了三个静态方法作为结构化并行的基本形式:

  • Parallel.Invoke方法:并行执行一组委托。
  • Parallel.For方法:执行与C# for循环等价的并行方法。
  • Parallel.ForEach方法:执行与C#foreach循环等价的并行方法。

这三个方法都会阻塞线程直到所有工作完成为止。和PLINQ一样,在出现未处理异常之后,其他的工作线程将会在其当前迭代完成之后停止,并将异常包装为AggregateException抛出给调用者。

 

1. 与PLINQ一样,Parallel.*方法是针对计算密集型任务而不是I/O密集型任务进行优化的。

2. 循环是无序的,如果需要顺序,直接使用同步。

3. 如果用到共享变量,要使用线程同步锁。

4. 如果虽是大量,但简单无等待的操作,同步执行可能更快。

5. 如果是带数据库操作,请使用事务,注意错误处理。

6. ForEach 比For效率更高。

 

一: Parallel.Invoke:并行执行一组Action委托,然后等待它们完成(尽可能的同时执行)。

    1. 该方法最简单的定义方式如下:public static void Invoke (params Action[] actions);

    2. 如果任务不超过10个,建议直接用Task,因为不超过10个的时候,Invoke内部会使用Task.Factory.StartNew创建任务。直接用Task.Factory.StartNew或者Task.Run效率更高。

    3. 适合用于执行大量且无返回值的场景:如果将一百万个委托传递给Parallel.Invoke方法,它仍然能够有效工作。这是因为该方法会将大量的元素划分为若干批次(自动分批处理),并将其分派给底层的Task。

复制代码
 1  #region 第一种模式
 2         static void ParallelInvoke1()
 3         {
 4             Parallel.Invoke(Fun1, Fun2);
 5         }
 6         static void Fun1()
 7         {
 8             Console.WriteLine("Fun1");
 9         }
10         static void Fun2()
11         {
12             Console.WriteLine("Fun2");
13         }
14         #endregion
15         #region 第二种模式
16         static void ParallelInvoke2()
17         {
18             Action action = () =>
19              {
20                  Console.WriteLine($"Thread Id:{Thread.CurrentThread.ManagedThreadId }");
21              };
22             Parallel.Invoke(action, action, action, action, action, action, action, action, action, action, action, action);
23             Console.WriteLine("Parallel.Invoke 执行完成");
24         }
25         #endregion
Parallel.Invoke
复制代码

 

二. Parallel.For使用(无序的),适合带索引的大量循环操作

复制代码
// 顺序
for(int i=0; i<100; i++)
    Foo(i);
 
// 并行话代码
Parallel.For(0, 100, i=> Foo(i));
// 简洁写法
Parallel.For(0, 100,Foo);
View Code
复制代码

 

Parallel Options选项配置,有三个属性
1. Cancellation Token 取消任务,并且处理任务被取消后的一些操作
2. MaxDegreeOfParallelism 设置最大并发数,默认-1
3. TaskScheduler指定任务调度器。


ParallelLoopResult 并发结果,有两个属性
1. IsCompleted 任务是否执行完成。
2. LowestBreakIteration 调用Break方法的最小任务索引(小于这个的索引都要执行)


ParallelLoopState,由于并行的For或ForEach的循环体是一个委托,因此无法使用break语句提前结束循环。但是可以调用ParallelLoopState对象的Break方法或Stop方法来跳出或者结束循环
Break
Stop

复制代码
 1 /// <summary>
 2         /// Parallel.For使用
 3         /// </summary>
 4         /// <param name="num"></param>
 5         static void ParallelForFun(int num)
 6         {
 7             Console.WriteLine($"ParallelFor执行总次数:{num}");
 8             var list = new List<dynamic>() ;
 9             ParallelLoopResult result = Parallel.For(0, num, i =>
10             {
11                 list.Add(new Product { Id = i, nameof = "Testname" });
12                 Console.WriteLine($"Task ID:{Task.CurrentId},Thread:{Thread.CurrentThread.ManagedThreadId }");
13                 Thread.Sleep(20);
14             });
15         }
View Code
复制代码

a. 循环中断:Parallel.For带ParallelLoopState(执行状态,可以中途取消),带ParallelLoopResult(执行结果)

复制代码
 1  static void ParallelForAsyncAbort()
 2         {
 3 
 4             ParallelLoopResult result =
 5                 Parallel.For(10, 100, async (int index, ParallelLoopState pls) =>
 6                   {
 7                       Console.WriteLine($"Index:{index} task:{Task.CurrentId},Thread:{Thread.CurrentThread.ManagedThreadId }");
 8                       await Task.Delay(10);
 9                       if (index > 30)
10                           pls.Break();
11                   });
12             Console.WriteLine($"Is completed:{result.IsCompleted} LowestBreakIteration:{result.LowestBreakIteration }");
13         }
View Code
复制代码

Break:不立马结束循环,而是等所有小于ParallelLoopResult.LowestBreakIteration 的迭代执行完,才结束循环。(用Break时,记得要用大于的条件判断)

Stop:不用等,可以更快捷的结束循环。
Break与Stop都不能停止已经开始的迭代,只可以防止新的迭代开始。

b.循环取消:

复制代码
 1 static void ParallelForCancel()
 2         {
 3 
 4             var cts = new CancellationTokenSource();
 5             cts.Token.Register(() => { Console.WriteLine($"*** token canceled"); });
 6             cts.CancelAfter(500);//执行500毫秒后取消
 7             try
 8             {
 9                 ParallelLoopResult result = Parallel.For(0, 100, new ParallelOptions()
10                 {
11                     CancellationToken = cts.Token,
12                 }, x =>
13                  {
14                      Console.WriteLine($"loop {x} started");
15                      int sum = 0;
16                      for (int i = 0; i < 100; i++)
17                      {
18                          Thread.Sleep(2);
19                          sum += 1;
20                      }
21                      Console.WriteLine($"loop {x} finished");
22                  });
23             }
24             catch (OperationCanceledException ex)
25             {
26                 Console.WriteLine(ex.Message);
27             }
28         }
循环500毫秒后取消
复制代码

执行结果,可以看出,取消后,已经开始的任务,还是会继续执行完成

 

三:Parallel.ForEach(大数据集的循环执行,例如:数组,集合,枚举)

复制代码
 1  // 执行结果是无序的
 2         static void ParallelForEach1()
 3         {
 4             string[] data = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten" };
 5             ParallelLoopResult result = Parallel.ForEach(data, a =>
 6                  {
 7                      Console.WriteLine(a);
 8                      
 9                  });
10            
11         }
12         static void ParallelForEach2()
13         {
14             string[] data = { "zero", "one", "two", "three", "four", "five", "six", "seven", "eight","nine","ten" };
15 
16             ParallelLoopResult result = Parallel.ForEach<string>(data,(str,psl,index)=>
17             {
18                 Console.WriteLine($"str:{str} index:{index}");
19 
20             });
21 
22         }
ParallelForEach
复制代码

结果是无序的

 

posted @   apple-hu  阅读(481)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示