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-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的值。