任务取消Task Cancellation

System.Threading.Tasks.TaskSystem.Threading.Tasks.Task<TResult> 类通过使用 .NET Framework 中的取消标记来支持取消。The System.Threading.Tasks.Task and System.Threading.Tasks.Task<TResult> classes support cancellation through the use of cancellation tokens in the .NET Framework. 有关详细信息,请参阅托管线程中的取消For more information, see Cancellation in Managed Threads. 在任务类中,取消涉及用户委托间的协作,这表示可取消的操作和请求取消的代码。In the Task classes, cancellation involves cooperation between the user delegate, which represents a cancelable operation and the code that requested the cancellation. 成功取消涉及调用 CancellationTokenSource.Cancel 方法的请求代码,以及及时终止操作的用户委托。A successful cancellation involves the requesting code calling the CancellationTokenSource.Cancel method, and the user delegate terminating the operation in a timely manner. 可以使用以下选项之一终止操作:You can terminate the operation by using one of these options:

  • 简单地从委托中返回。By simply returning from the delegate. 在许多情况下,这样已足够;但是,采用这种方式取消的任务实例会转换为 TaskStatus.RanToCompletion 状态,而不是 TaskStatus.Canceled 状态。In many scenarios this is sufficient; however, a task instance that is canceled in this way transitions to the TaskStatus.RanToCompletion state, not to the TaskStatus.Canceled state.

  • 引发 OperationCanceledException ,并将其传递到在其上请求了取消的标记。By throwing a OperationCanceledException and passing it the token on which cancellation was requested. 完成此操作的首选方式是使用 ThrowIfCancellationRequested 方法。The preferred way to do this is to use the ThrowIfCancellationRequested method. 采用这种方式取消的任务会转换为 Canceled 状态,调用代码可使用该状态来验证任务是否响应了其取消请求。A task that is canceled in this way transitions to the Canceled state, which the calling code can use to verify that the task responded to its cancellation request.

下面的示例演示引发异常的任务取消的基本模式。The following example shows the basic pattern for task cancellation that throws the exception. 请注意,标记将传递到用户委托和任务实例本身。Note that the token is passed to the user delegate and to the task instance itself.

using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
    static void Main()
    {
        var tokenSource2 = new CancellationTokenSource();
        CancellationToken ct = tokenSource2.Token;

        var task = Task.Factory.StartNew(() =>
        {

            // 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 StartNew.

        tokenSource2.Cancel();

        // Just continue on this thread, or Wait/WaitAll with try-catch:
        try
        {
            task.Wait();
        }
        catch (AggregateException e)
        {
            foreach (var v in e.InnerExceptions)
                Console.WriteLine(e.Message + " " + v.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

有关更完整的示例,请参阅如何:取消任务及其子级For a more complete example, see How to: Cancel a Task and Its Children.

当任务实例观察到用户代码引发的 OperationCanceledException 时,它会将该异常的标记与其关联的标记(传递到创建任务的 API 的标记)进行比较。When a task instance observes an OperationCanceledException thrown by user code, it compares the exception's token to its associated token (the one that was passed to the API that created the Task). 如果这两个标记相同,并且标记的 IsCancellationRequested 属性返回 true,则任务会将此解释为确认取消并转换为 Canceled 状态。If they are the same and the token's IsCancellationRequested property returns true, the task interprets this as acknowledging cancellation and transitions to the Canceled state. 如果您不使用 WaitWaitAll 方法来等待任务,则任务只会将其状态设置为 CanceledIf you do not use a Wait or WaitAll method to wait for the task, then the task just sets its status to Canceled.

如果在等待的任务转换为“已取消”状态,就会抛出 System.Threading.Tasks.TaskCanceledException 异常(包装在 AggregateException 异常中)。If you are waiting on a Task that transitions to the Canceled state, a System.Threading.Tasks.TaskCanceledException exception (wrapped in an AggregateException exception) is thrown. 请注意,此异常指示成功的取消,而不是有错误的情况。Note that this exception indicates successful cancellation instead of a faulty situation. 因此,任务的 Exception 属性返回 nullTherefore, the task's Exception property returns null.

如果标记的 IsCancellationRequested 属性返回 false,或者异常的标记与任务的标记不匹配,则会将 OperationCanceledException 按照普通的异常来处理,从而导致任务转换为 Faulted 状态。If the token's IsCancellationRequested property returns false or if the exception's token does not match the Task's token, the OperationCanceledException is treated like a normal exception, causing the Task to transition to the Faulted state. 另外还要注意,其他异常的存在将也会导致任务转换为 Faulted 状态。Also note that the presence of other exceptions will also cause the Task to transition to the Faulted state. 您可以在 Status 属性中获取已完成任务的状态。You can get the status of the completed task in the Status property.

在请求取消操作之后,任务可能还可以继续处理一些项目。It is possible that a task may continue to process some items after cancellation is requested.

请参阅See Also

托管线程中的取消Cancellation in Managed Threads
如何:取消任务及其子级How to: Cancel a Task and Its Children