更新:
在C# 5.0後,await所產生的取消Task異常會直街拋出異常。比如下面的代碼,在C# 5.0/.NET 4.5中等效於:
var src = new CancellationTokenSource();
src.CancelAfter(100);
try
{
await Task.Factory.StartNew(() =>
{
Thread.Sleep(1000);
src.Token.ThrowIfCancellationRequested();
}, src.Token);
}
catch (Exception ex)
{
Console.WriteLine(ex.GetType());
}
而輸出會是:
System.OperationCanceledException
對於Task.Wait相關的調用,取消後的OperationCanceledException異常會成為TaskCanceledException並且會被封裝在AggregateException中。
var src = new CancellationTokenSource();
var task = Task.Factory.StartNew(() =>
{
Thread.Sleep(1000);
src.Token.ThrowIfCancellationRequested();
}, src.Token);
src.Cancel();
try
{
task.Wait();
}
catch (AggregateException ae)
{
foreach (var ex in ae.InnerExceptions)
Console.WriteLine(ex.GetType());
}
輸出:
System.Threading.Tasks.TaskCanceledException
當然可以手動拋出一個OperationCanceledException異常,注意在建構函式中傳入CancellationToken對象。
var src = new CancellationTokenSource();
var task = Task.Factory.StartNew(() =>
{
Thread.Sleep(500);
throw new OperationCanceledException("操作被取消", src.Token);
}, src.Token);
可以達到同樣的效果。
對於Parallel和PLINQ,取消後的並存執行結果總會是立即拋出OperationCanceledException。
另外注意在Parallel中的異常處理和Task也有較大區別,可以參考這篇文章:.NET(C#) TPL:Parallel迴圈和多個Task的異常處理
示範在Parallel類中取消操作:
var opt = new ParallelOptions();
var src = new CancellationTokenSource();
opt.CancellationToken = src.Token;
Task.Factory.StartNew(() =>
{
Thread.Sleep(700);
src.Cancel();
});
try
{
Parallel.For(1, 11, opt, i =>
{
opt.CancellationToken.ThrowIfCancellationRequested();
Thread.Sleep(500);
Console.WriteLine("迭代:" + i);
});
}
catch (OperationCanceledException)
{
Console.WriteLine("操作取消");
}
可能的輸出:
迭代:1
迭代:7
迭代:5
迭代:3
迭代:2
迭代:6
迭代:8
迭代:4
操作取消
PLINQ中類似,需要使用ParallelEnumerable.WithCancellation方法,然後將CancellationToken傳入就可以了。
var src = new CancellationTokenSource(
Task.Factory.StartNew(() =>
{
Thread.Sleep(500);
src.Cancel();
});
try
{
var res = Enumerable.Range(1, 100)
.AsParallel()
.WithCancellation(src.Token)
.Select(i =>
{
Thread.Sleep(50);
return i;
});
foreach (var i in res)
Console.WriteLine(i);
}
catch (OperationCanceledException)
{
Console.WriteLine("操作被取消");
}
輸出:
操作被取消