任务取消

System.Threading.Tasks.TaskSystem.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,则任务会将此解释为确认取消并转换为“已取消”状态。 如果不使用 WaitWaitAll 方法来等待任务,则任务只会将其状态设置为 Canceled

如果在等待的任务转换为“已取消”状态,就会引发 System.Threading.Tasks.TaskCanceledException 异常(包装在 AggregateException 异常中)。 此异常表示成功取消,而不是有错误的情况。 因此,任务的 Exception 属性返回 null

如果标记的 IsCancellationRequested 属性返回 false,或者异常的标记与任务的标记不匹配,则会将 OperationCanceledException 按照普通的异常来处理,从而导致任务转换为“已出错”状态。 其他异常的存在将也会导致任务转换为“已出错”状态。 您可以在 Status 属性中获取已完成任务的状态。

在请求取消操作之后,任务可能还会继续处理一些项。

请参阅