《C#并行编程高级教程》-第三章:命令式任务并行
主题:System.Threading.Tasks.Task
一个Task表示一个一步操作。
任务与线程没有一对一的关系:并不是每新建一个task就会新建一个线程,创建的新task会从现有的线程中窃取工作机会。只有在必要的时候,通用语言运行时(CLR)才会创建必要的线程。
Task重要的只读属性:Exceotion若有异常则不为null、IsCancled是否被取消、IsCompleted是否完成、IsFaulted是否因为未处理的异常而终止、Status:TaskStatus值,表状态
Task初始状态:Created,WaitingForActivation,WaitingToRun
Task最终状态:Canceled,Faulted,RanToCompletion
启动任务:task.Start(); 以一种独立的方式对委托的执行进行初始化
Wait:静态方法:Task.WaitAll(Task[]):当前线程会等待Task[]中所有的任务执行完成;
实力方法:task.Wait();当前线程会等待到task完成
通过取消标记取消任务
取消标记:System.Threading.CancellationToken
应用:通过调用System.Threading.CancellationToken.ThrowIfCancellationRequested方法抛出良性异常OperationCanceledExcepion来取消任务。通过这种方式,Task实力会转入TaskStatus.Canceld状态,并且IsCanceled属性会被设置为true
private static void Fun1(System.Threading.CancellationToken ct)
{
ct.ThrowIfCancellationRequested;
//some code
//假如有for
for( )
{
//......
ct.ThrowIfCancellationRequested();
//....
}
//....
}
private static void Fun2(System.Threading.CancellationToken ct)
{
ct.ThrowIfCancellationRequested;
//some code
//假如有for
for( )
{
//......
ct.ThrowIfCancellationRequested();
//....
}
//....
}
//..........
static void Main(string[] args)
{
var cts=new System.Threading.CancellationTokenSource();
var ct=cts.Token;
var t1=Task.Factory.StartNew( ()=>Fun1(ct), ct);
var t2=Task.Factory.StartNew( ()=>Fun2(ct), ct);
//....
cts.Cancel();
//........
}
CancellationTokenSource能够初始化取消请求,而CancellationToken能够将这些请求传递给异步的操作。
StartNew在功能上与使用Task构造函数创建Task并调用Start来调度其执行是等价的。
异常处理
若有异常,则IsFaulted属性被置为true。前面提到的用来取消的异常不会导致为true。
try
{
//....
Task.WaitAll(new Task[] {t1,t2},1000)
//....
}
catch(AggregateException ex)
{
foreach(....)
}
从任务返回值
若任务执行的函数有返回值,则返回值的属性可以通过任务实例的Result属性来访问:t1.Result,Result的类型,编译器会根据函数返回值自动推导出。
在用StartNew创建任务的时候(使用new Task()不行),可以使用TaskCreationOptions参数指定一些任务相关的行为。
var t1=Task.Factory.StartNew( ()=> {
//lambda...}, TaskCreationOptions.LongRunning );
这表示t1会运行比较长的时间。
重点:通过延续串联多个任务
方式:在任务实例上调用ContinueWith方法
如果下一个任务要以上一个任务的结果作为输入才能运行就可以采用这种方法来解决。
var t2=t1.ContinedWith( (t)=>{
//do someting with the result of t1
//...eg
for(int i=0;i<t.Result.Count;i++)
{
Console.Write(t.Result[i]);
}
});
lambda中的t表示的是t1。可以串联很多任务,然后等待最后一个任务完成,t2后面还可以串联任务。
也可以一个任务串联多个任务,比如t2,t3都串联到t1后面。
在串联的时候,也有像TaskCreationOptions一样的选项:TaskContinuationOptions,其类型更加丰富,见MSDN。如果没有指定选项,那么默认的选项就是TaskContinuationOptions.None,这样,即使之前的任务(t1)取消了,那么延续的任务仍会被调度并启动执行。
对于多任务延续时,不能使用以下值:TaskContinuationOptions.NotOnRanToCompletion,TaskContinuationOptions.NotOnFaulted,TaskContinuationOptions.NotOnCanceled,TaskContinuationOptions.OnlyOnRanToCompletion,TaskContinuationOptions.OnlyOnFaulted,TaskContinuationOptions.OnlyOnCanceled,
延续是一项功能非常强大的技术。如果知道一个任务或多个任务只有在另一个任务完成之后才可以运行,那么正确的做法是通过延续将这些任务串联起来。底层调度器能够充分利用工作窃取机制,并且能够根据运行时已有的资源对调度机制进行优化。
延续之所以重要,不仅仅是因为它能够简化代码,而且还因为它能够帮助调度器对很快就要启动的任务采取正确的操作。