29.2.2 For方法重载二

第二个重载:For(Int32,Int32,ParallelOptions,Action<Int32,ParallelLoopState>),其完整签名如下:


public static ParallelLoopResult For(

int fromInclusive,

int toExclusive,

ParallelOptions parallelOptions,

Action<int,ParallelLoopState>body


通过此重载版本可以学到的知识如下:

❑ParallelOptions类型的简单应用,更详细的应用请参考第29.6.1节。关于ParallelOptions需要重点了解的是它的3个属性,如表29-3所示。

29.2.2 For方法重载二 - 图1

代码清单29-4 演示了ParallelOptions类中CancellationToken和MaxDegreeOfParallelism两个属性的使用方法。示例代码演示了如何使用CancellationTokenSource和CancellationToken来取消一个任务的运行,这相较于之前使用ThreadPool无法对执行任务的工作线程进行更细粒度的控制是一个较大进步。方法也很简单。

(1)首先声明并实例化一个CancellationTokenSource类型的实例;

(2)通过CancellationTokenSource类型的实例获取CancellationToken并赋给ParallelOptions的实例的对应属性;

(3)将ParallelOptions实例传给Parallel.For或Parallel.Invoke,在需要取消Parallel.For时只需要简单地调用CancellationTokenSource类型的实例的Cancel方法,不同的是,在并行操作内部调用会引发OperationCanceledException异常,在外部调用则需要在操作内部配合使用CancellationToken.IsCancellationRequested进行检查。

代码清单29-4 For(Int32,Int32,ParallelOptions,Action<Int32,ParallelLoopState>)重载示例代码


using System;

using System.Threading;

using System.Threading.Tasks;

namespace ProgrammingCSharp4

{

class ParallelSample

{

static void Main(string[]args)

{

CancellationTokenSource cancelSource=

new CancellationTokenSource();

ParallelOptions options=new ParallelOptions();

options.CancellationToken=cancelSource.Token;

options.MaxDegreeOfParallelism=2;

try

{

ParallelLoopResult result=Parallel.For(0,

100,

options,

(i,state)=>

{

if(i==5)

{

cancelSource.Cancel();

}

//模拟需要较长时间运行的任务

for(int j=0;j<10;j++)

{

Thread.Sleep(100);

if(state.ShouldExitCurrentIteration)

}

Console.WriteLine(

"[{0}]Thread[{1}]:{2}",

DateTime.Now.ToString("hh:mm:ss fff"),

Thread.CurrentThread.ManagedThreadId,i);

});

if(result.IsCompleted)

{

Console.WriteLine(“所有操作执行完成!”);

}

return;

Console.WriteLine(

"result.IsCompleted={0}",

result.IsCompleted);

Console.WriteLine(

"result.LowestBreakIteration={0}",

result.LowestBreakIteration);

}

catch(OperationCanceledException e)

{

Console.WriteLine(“操作已被取消!”);

}

}

}

}


上述代码的运行结果为:


[11:49:36 153]Thread[3]:50

[11:49:36 153]Thread[1]:0

[11:49:37 325]Thread[1]:1

[11:49:37 325]Thread[4]:51

[11:49:38 419]Thread[1]:2

[11:49:38 419]Thread[4]:52

[11:49:39 512]Thread[1]:3

[11:49:39 512]Thread[3]:53

[11:49:40 606]Thread[1]:4

[11:49:40 606]Thread[3]:54

操作已被取消!

请按任意键继续……


我们在示例代码中的本意是当i自增到5时取消操作执行,因为代码是并行执行,因此当一个线程在自增到5的时候,位于其他处理器上的线程也在同时计算,因此看到了上述运行的结果。当把MaxDegreeOfParallelism由2改为1时,多处理器并行计算就成了单处理器串行计算了,结果如下:


[11:55:50 747]Thread[1]:0

[11:55:51 903]Thread[1]:1

[11:55:52 997]Thread[1]:2

[11:55:54 091]Thread[1]:3

[11:55:55 184]Thread[1]:4

操作已被取消!

请按任意键继续……


另一方面,在取消并行操作时都触发了OperationCanceledException异常,这是因为在操作内部执行了取消操作。如果把取消操作放在并行操作的内部,则操作内需要配合对CancellationToken.IsCancellationRequested的检查,该值为true表示操作被取消,如代码清单29-5所示。

代码清单29-5 在操作外部取消操作的执行


using System;

using System.Threading;

using System.Threading.Tasks;

namespace ProgrammingCSharp4

{

class ParallelSample

{

static void Main(string[]args)

{

CancellationTokenSource cancelSource=

new CancellationTokenSource();

ParallelOptions options=new ParallelOptions();

options.CancellationToken=cancelSource.Token;

options.MaxDegreeOfParallelism=2;

try

{

ParallelLoopResult result=Parallel.For(0,

10,

options,

(i,state)=>

{

//检查是否被取消

if(!options.CancellationToken.IsCancellationRequested)

{

Console.WriteLine(

"[{0}]Thread[{1}]:{2}",

DateTime.Now.ToString("hh:mm:ss fff"),

Thread.CurrentThread.ManagedThreadId,i);

}

});

if(result.IsCompleted)

{

Console.WriteLine(“所有操作执行完成!”);

}

//在操作外部取消操作

cancelSource.Cancel();

Console.WriteLine(

"result.IsCompleted={0}",

result.IsCompleted);

Console.WriteLine(

"result.LowestBreakIteration={0}",

result.LowestBreakIteration);

}

catch(OperationCanceledException e)

{

Console.WriteLine(“操作已被取消!”);

}

}

}

}


上述代码的运行结果为:


[10:46:34 707]Thread[3]:0

[10:46:34 754]Thread[3]:1

[10:46:34 754]Thread[3]:2

[10:46:34 754]Thread[3]:3

[10:46:34 754]Thread[3]:4

[10:46:34 754]Thread[3]:6

[10:46:34 754]Thread[3]:7

[10:46:34 754]Thread[3]:8

[10:46:34 754]Thread[3]:9

[10:46:34 707]Thread[1]:5

所有操作执行完成!

result.IsCompleted=True

result.LowestBreakIteration=

请按任意键继续……


可见,OperationCanceledException异常没有再次被触发,取而代之的是在操作内部检查options.CancellationToken.IsCancellationRequested的值。