任务取消
System.Threading.Tasks.Task 和 System.Threading.Tasks.Task<TResult> 类支持使用取消令牌进行取消。 有关详细信息,请参阅托管线程中的取消。 在 Task 类中,取消涉及用户委托间的协作,这表示可取消的操作和请求取消的代码。 成功取消涉及调用 CancellationTokenSource.Cancel 方法的请求代码,以及及时终止操作的用户委托。 可以使用以下选项之一终止操作:
从委托中返回。 在许多情况下,此选项就足够了。 但是,采用这种方式取消的任务实例会转换为 TaskStatus.RanToCompletion 状态,而不是 TaskStatus.Canceled 状态。
引发 OperationCanceledException,并将其传递到在其上请求了取消的标记。 完成此操作的首选方式是使用 ThrowIfCancellationRequested 方法。 采用这种方式取消的任务会转换为 Canceled 状态,调用代码可使用该状态来验证任务是否响应了其取消请求。
下面的示例演示引发异常的任务取消的基本模式:
注意
标记将传递到用户委托和任务实例。
using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
var tokenSource2 = new CancellationTokenSource();
CancellationToken ct = tokenSource2.Token;
var task = Task.Run(() =>
{
// Were we already canceled?
ct.ThrowIfCancellationRequested();
bool moreToDo = true;
while (moreToDo)
{
// Poll on this property if you have to do
// other cleanup before throwing.
if (ct.IsCancellationRequested)
{
// Clean up here, then...
ct.ThrowIfCancellationRequested();
}
}
}, tokenSource2.Token); // Pass same token to Task.Run.
tokenSource2.Cancel();
// Just continue on this thread, or await with try-catch:
try
{
await task;
}
catch (OperationCanceledException e)
{
Console.WriteLine($"{nameof(OperationCanceledException)} thrown with message: {e.Message}");
}
finally
{
tokenSource2.Dispose();
}
Console.ReadKey();
}
}
Imports System.Threading
Imports System.Threading.Tasks
Module Test
Sub Main()
Dim tokenSource2 As New CancellationTokenSource()
Dim ct As CancellationToken = tokenSource2.Token
Dim t2 = Task.Factory.StartNew(Sub()
' Were we already canceled?
ct.ThrowIfCancellationRequested()
Dim moreToDo As Boolean = True
While moreToDo = True
' Poll on this property if you have to do
' other cleanup before throwing.
If ct.IsCancellationRequested Then
' Clean up here, then...
ct.ThrowIfCancellationRequested()
End If
End While
End Sub _
, tokenSource2.Token) ' Pass same token to StartNew.
' Cancel the task.
tokenSource2.Cancel()
' Just continue on this thread, or Wait/WaitAll with try-catch:
Try
t2.Wait()
Catch e As AggregateException
For Each item In e.InnerExceptions
Console.WriteLine(e.Message & " " & item.Message)
Next
Finally
tokenSource2.Dispose()
End Try
Console.ReadKey()
End Sub
End Module
有关完整示例,请参阅如何:取消任务及其子级。
当任务实例观察到用户代码引发的 OperationCanceledException 时,它会将该异常的标记与其关联的标记(传递到创建任务的 API 的标记)进行比较。 如果标记相同,并且标记的 IsCancellationRequested 属性返回 true
,则任务会将此解释为确认取消并转换为“已取消”状态。 如果不使用 Wait 或 WaitAll 方法来等待任务,则任务只会将其状态设置为 Canceled。
如果在等待的任务转换为“已取消”状态,就会引发 System.Threading.Tasks.TaskCanceledException 异常(包装在 AggregateException 异常中)。 此异常表示成功取消,而不是有错误的情况。 因此,任务的 Exception 属性返回 null
。
如果标记的 IsCancellationRequested 属性返回 false
,或者异常的标记与任务的标记不匹配,则会将 OperationCanceledException 按照普通的异常来处理,从而导致任务转换为“已出错”状态。 其他异常的存在将也会导致任务转换为“已出错”状态。 您可以在 Status 属性中获取已完成任务的状态。
在请求取消操作之后,任务可能还会继续处理一些项。