托管线程中的取消Cancellation in Managed Threads

从 .NET Framework 4 开始,.NET Framework 在协作取消异步操作或长时间运行的同步操作时使用统一的模型。Starting with the .NET Framework 4, the .NET Framework uses a unified model for cooperative cancellation of asynchronous or long-running synchronous operations. 此模型基于被称为取消标记的轻量对象。This model is based on a lightweight object called a cancellation token. 调用一个或多个可取消操作的对象(例如通过创建新线程或任务)将标记传递给每个操作。The object that invokes one or more cancelable operations, for example by creating new threads or tasks, passes the token to each operation. 单个操作反过来可将标记的副本传递给其他操作。Individual operations can in turn pass copies of the token to other operations. 稍后,创建标记的对象可使用此标记请求停止执行操作内容。At some later time, the object that created the token can use it to request that the operations stop what they are doing. 只有发出请求的对象,才能发出取消请求,而每个侦听器负责侦听是否有请求,并及时适当地响应请求。Only the requesting object can issue the cancellation request, and each listener is responsible for noticing the request and responding to it in an appropriate and timely manner.

用于实现协作取消模型的常规模式是:The general pattern for implementing the cooperative cancellation model is:

重要

CancellationTokenSource 类实现 IDisposable 接口。The CancellationTokenSource class implements the IDisposable interface. 使用取消标记源释放所包含的任何非托管资源后,应确保调用 CancellationTokenSource.Dispose 方法。You should be sure to call the CancellationTokenSource.Dispose method when you have finished using the cancellation token source to free any unmanaged resources it holds.

下图显示了标记源与标记的所有副本之间的关系。The following illustration shows the relationship between a token source and all the copies of its token.

CancellationTokenSource 和 cancellation 令牌CancellationTokenSource and cancellation tokens

新的取消模型使创建取消感知应用程序和库更简单,并支持以下功能:The new cancellation model makes it easier to create cancellation-aware applications and libraries, and it supports the following features:

  • 取消具有协作性,且不会在侦听器上强制执行。Cancellation is cooperative and is not forced on the listener. 侦听器确定如何适当地以响应取消请求终止操作。The listener determines how to gracefully terminate in response to a cancellation request.

  • 请求与侦听不同。Requesting is distinct from listening. 调用可取消操作的对象可以控制何时(如果有)请求取消。An object that invokes a cancelable operation can control when (if ever) cancellation is requested.

  • 请求对象仅使用一种方法调用,向标记的所有副本发出取消请求。The requesting object issues the cancellation request to all copies of the token by using just one method call.

  • 侦听器可以将多个令牌联接到一个链接令牌 ,从而同时侦听多个令牌。A listener can listen to multiple tokens simultaneously by joining them into one linked token.

  • 用户代码可以注意并响应来自库代码的取消请求,而库代码可以注意并响应来自用户代码的取消请求。User code can notice and respond to cancellation requests from library code, and library code can notice and respond to cancellation requests from user code.

  • 侦听器可通过轮询、回调注册或等待等待句柄来接收到取消请求的通知。Listeners can be notified of cancellation requests by polling, callback registration, or waiting on wait handles.

取消类型Cancellation Types

取消框架作为相关类型集实现,如下表所列。The cancellation framework is implemented as a set of related types, which are listed in the following table.

类型名称Type name 说明Description
CancellationTokenSource 创建取消标记并为此标记的所有副本发出取消请求的对象。Object that creates a cancellation token, and also issues the cancellation request for all copies of that token.
CancellationToken 通常作为方法参数传递给一个或多个侦听器的轻量值类型。Lightweight value type passed to one or more listeners, typically as a method parameter. 侦听器通过轮询、回调或等待句柄监视标记的 IsCancellationRequested 属性的值。Listeners monitor the value of the IsCancellationRequested property of the token by polling, callback, or wait handle.
OperationCanceledException 此异常的构造函数的重载将 CancellationToken 作为参数接受。Overloads of this exception's constructor accept a CancellationToken as a parameter. 侦听器可能会选择性地引发此异常,议验证取消源并通知其他侦听器它已响应取消请求。Listeners can optionally throw this exception to verify the source of the cancellation and notify others that it has responded to a cancellation request.

新的取消模型以多种类型集成到 .NET Framework 中。The new cancellation model is integrated into the .NET Framework in several types. 最重要的类型包括 System.Threading.Tasks.ParallelSystem.Threading.Tasks.TaskSystem.Threading.Tasks.Task<TResult>System.Linq.ParallelEnumerableThe most important ones are System.Threading.Tasks.Parallel, System.Threading.Tasks.Task, System.Threading.Tasks.Task<TResult> and System.Linq.ParallelEnumerable. 建议将此新取消模型用于所有新的库和应用代码。We recommend that you use this new cancellation model for all new library and application code.

代码示例Code Example

在以下示例中,请求对象创建 CancellationTokenSource 对象,然后传递其 Token 属性到可取消操作中。In the following example, the requesting object creates a CancellationTokenSource object, and then passes its Token property to the cancelable operation. 接收请求的操作通过轮询监视标记的 IsCancellationRequested 属性的值。The operation that receives the request monitors the value of the IsCancellationRequested property of the token by polling. 值变为 true 后,侦听器可以适当方式终止操作。When the value becomes true, the listener can terminate in whatever manner is appropriate. 在此示例中,方法只需退出,很多情况下都只需执行此操作。In this example, the method just exits, which is all that is required in many cases.

备注

此示例使用 QueueUserWorkItem 方法演示新的取消框架与旧版 API 兼容。The example uses the QueueUserWorkItem method to demonstrate that the new cancellation framework is compatible with legacy APIs. 有关使用新的首选 System.Threading.Tasks.Task 类型的示例,请参阅如何:取消任务及其子级For an example that uses the new, preferred System.Threading.Tasks.Task type, see How to: Cancel a Task and Its Children.

using System;
using System.Threading;

public class Example
{
   public static void Main()
   {
      // Create the token source.
      CancellationTokenSource cts = new CancellationTokenSource();

      // Pass the token to the cancelable operation.
      ThreadPool.QueueUserWorkItem(new WaitCallback(DoSomeWork), cts.Token);
      Thread.Sleep(2500);

      // Request cancellation.
      cts.Cancel();
      Console.WriteLine("Cancellation set in token source...");
      Thread.Sleep(2500);
      // Cancellation should have happened, so call Dispose.
      cts.Dispose();
   }

   // Thread 2: The listener
   static void DoSomeWork(object obj)
   {
      CancellationToken token = (CancellationToken)obj;

      for (int i = 0; i < 100000; i++) {
         if (token.IsCancellationRequested)
         {
            Console.WriteLine("In iteration {0}, cancellation has been requested...",
                              i + 1);
            // Perform cleanup if necessary.
            //...
            // Terminate the operation.
            break;
         }
         // Simulate some work.
         Thread.SpinWait(500000);
      }
   }
}
// The example displays output like the following:
//       Cancellation set in token source...
//       In iteration 1430, cancellation has been requested...
Imports System.Threading

Module Example
   Public Sub Main()
      ' Create the token source.
      Dim cts As New CancellationTokenSource()

      ' Pass the token to the cancelable operation.
      ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf DoSomeWork), cts.Token)
      Thread.Sleep(2500)
        
      ' Request cancellation by setting a flag on the token.
      cts.Cancel()
      Console.WriteLine("Cancellation set in token source...")
      Thread.Sleep(2500)
      ' Cancellation should have happened, so call Dispose.
      cts.Dispose()
   End Sub

   ' Thread 2: The listener
   Sub DoSomeWork(ByVal obj As Object)
      Dim token As CancellationToken = CType(obj, CancellationToken)
      
      For i As Integer = 0 To 1000000
         If token.IsCancellationRequested Then
            Console.WriteLine("In iteration {0}, cancellation has been requested...",
                              i + 1)
            ' Perform cleanup if necessary.
            '...
            ' Terminate the operation.
            Exit For
         End If
      
         ' Simulate some work.
         Thread.SpinWait(500000)
      Next
   End Sub
End Module
' The example displays output like the following:
'       Cancellation set in token source...
'       In iteration 1430, cancellation has been requested...

操作取消与对象取消Operation Cancellation Versus Object Cancellation

在新的取消框架中,取消将引用操作(而不是对象)。In the new cancellation framework, cancellation refers to operations, not objects. 取消请求意味着应在执行任何所需的清理后尽快停止操作。The cancellation request means that the operation should stop as soon as possible after any required cleanup is performed. 一个取消标记应代指一个“可取消操作”,但可在程序中实现此操作。One cancellation token should refer to one "cancelable operation," however that operation may be implemented in your program. 在标记的 IsCancellationRequested 属性设置为 true 后,不能重置为 falseAfter the IsCancellationRequested property of the token has been set to true, it cannot be reset to false. 因此,取消后不能重用取消标记。Therefore, cancellation tokens cannot be reused after they have been canceled.

如果需要对象取消机制,可以通过调用 CancellationToken.Register 方法将其基于操作取消机制,如以下示例所示。If you require an object cancellation mechanism, you can base it on the operation cancellation mechanism by calling the CancellationToken.Register method, as shown in the following example.

using System;
using System.Threading;

class CancelableObject
{
   public string id;
   
   public CancelableObject(string id)
   {
      this.id = id;
   }
   
   public void Cancel() 
   { 
      Console.WriteLine("Object {0} Cancel callback", id);
      // Perform object cancellation here.
   }
}

public class Example
{
   public static void Main()
   {
      CancellationTokenSource cts = new CancellationTokenSource();
      CancellationToken token = cts.Token;

      // User defined Class with its own method for cancellation
      var obj1 = new CancelableObject("1");
      var obj2 = new CancelableObject("2");
      var obj3 = new CancelableObject("3");

      // Register the object's cancel method with the token's
      // cancellation request.
      token.Register(() => obj1.Cancel());
      token.Register(() => obj2.Cancel());
      token.Register(() => obj3.Cancel());

      // Request cancellation on the token.
      cts.Cancel();
      // Call Dispose when we're done with the CancellationTokenSource.
      cts.Dispose();
   }
}
// The example displays the following output:
//       Object 3 Cancel callback
//       Object 2 Cancel callback
//       Object 1 Cancel callback
Imports System.Threading

Class CancelableObject
   Public id As String
   
   Public Sub New(id As String)
      Me.id = id
   End Sub
   
   Public Sub Cancel() 
      Console.WriteLine("Object {0} Cancel callback", id)
      ' Perform object cancellation here.
   End Sub
End Class

Module Example
   Public Sub Main()
        Dim cts As New CancellationTokenSource()
        Dim token As CancellationToken = cts.Token

        ' User defined Class with its own method for cancellation
        Dim obj1 As New CancelableObject("1")
        Dim obj2 As New CancelableObject("2")
        Dim obj3 As New CancelableObject("3")

        ' Register the object's cancel method with the token's
        ' cancellation request.
        token.Register(Sub() obj1.Cancel())
        token.Register(Sub() obj2.Cancel())
        token.Register(Sub() obj3.Cancel())

        ' Request cancellation on the token.
        cts.Cancel()
        ' Call Dispose when we're done with the CancellationTokenSource.
        cts.Dispose()
   End Sub
End Module
' The example displays output like the following:
'       Object 3 Cancel callback
'       Object 2 Cancel callback
'       Object 1 Cancel callback

如果对象支持多个并发可取消操作,则将单独的标记作为输入传递给每个非重复的可取消操作。If an object supports more than one concurrent cancelable operation, pass a separate token as input to each distinct cancelable operation. 这样,无需影响其他操作即可取消某项操作。That way, one operation can be cancelled without affecting the others.

侦听和响应取消请求Listening and Responding to Cancellation Requests

在用户委托中,可取消操作的实施者确定如何以响应取消请求来终止操作。In the user delegate, the implementer of a cancelable operation determines how to terminate the operation in response to a cancellation request. 在很多情况下,用户委托只需执行全部所需清理,然后立即返回。In many cases, the user delegate can just perform any required cleanup and then return immediately.

但是,在更复杂的情况下,用户委托可能需要通知库代码已发生取消。However, in more complex cases, it might be necessary for the user delegate to notify library code that cancellation has occurred. 在这种情况下,终止操作的正确方式是委托调用 ThrowIfCancellationRequested 方法,这将引发 OperationCanceledExceptionIn such cases, the correct way to terminate the operation is for the delegate to call the ThrowIfCancellationRequested, method, which will cause an OperationCanceledException to be thrown. 库代码可以在用户委托线程上捕获此异常,并检查异常的标记以确定异常是否表示协作取消或一些其他的异常情况。Library code can catch this exception on the user delegate thread and examine the exception's token to determine whether the exception indicates cooperative cancellation or some other exceptional situation.

Task 类以此方式处理 OperationCanceledExceptionThe Task class handles OperationCanceledException in this way. 有关详细信息,请参阅任务取消For more information, see Task Cancellation.

通过轮询进行侦听Listening by Polling

对于循环或递归的长时间运行的计算,可以通过定期轮询 CancellationToken.IsCancellationRequested 属性的值来侦听取消请求。For long-running computations that loop or recurse, you can listen for a cancellation request by periodically polling the value of the CancellationToken.IsCancellationRequested property. 如果其值为 true,则此方法应尽快清理并终止。If its value is true, the method should clean up and terminate as quickly as possible. 最佳的轮询频率取决于应用程序的类型。The optimal frequency of polling depends on the type of application. 由开发人员决定任一给定程序的最佳轮询频率。It is up to the developer to determine the best polling frequency for any given program. 轮询本身不会显著影响性能。Polling itself does not significantly impact performance. 以下示例演示了一种轮询方法。The following example shows one possible way to poll.

static void NestedLoops(Rectangle rect, CancellationToken token)
{
   for (int x = 0; x < rect.columns && !token.IsCancellationRequested; x++) {
      for (int y = 0; y < rect.rows; y++) {
         // Simulating work.
         Thread.SpinWait(5000);
         Console.Write("{0},{1} ", x, y);
      }

      // Assume that we know that the inner loop is very fast.
      // Therefore, checking once per row is sufficient.
      if (token.IsCancellationRequested) {
         // Cleanup or undo here if necessary...
         Console.WriteLine("\r\nCancelling after row {0}.", x);
         Console.WriteLine("Press any key to exit.");
         // then...
         break;
         // ...or, if using Task:
         // token.ThrowIfCancellationRequested();
      }
   }
}
Shared Sub NestedLoops(ByVal rect As Rectangle, ByVal token As CancellationToken)
    For x As Integer = 0 To rect.columns
        For y As Integer = 0 To rect.rows
            ' Simulating work.
            Thread.SpinWait(5000)
            Console.Write("0' end block,1' end block ", x, y)
        Next

        ' Assume that we know that the inner loop is very fast.
        ' Therefore, checking once per row is sufficient.
        If token.IsCancellationRequested = True Then
            ' Cleanup or undo here if necessary...
            Console.WriteLine(vbCrLf + "Cancelling after row 0' end block.", x)
            Console.WriteLine("Press any key to exit.")
            ' then...
            Exit For
            ' ...or, if using Task:
            ' token.ThrowIfCancellationRequested()
        End If
    Next
End Sub

有关更完整的示例,请参见如何:通过轮询侦听取消请求For a more complete example, see How to: Listen for Cancellation Requests by Polling.

通过注册回调进行侦听Listening by Registering a Callback

某些操作可能被阻止,导致其无法及时检查取消标记的值。Some operations can become blocked in such a way that they cannot check the value of the cancellation token in a timely manner. 对于这些情况,可以注册在接收取消请求时取消阻止此方法的回调方法。For these cases, you can register a callback method that unblocks the method when a cancellation request is received.

Register 方法返回专用于此目的的 CancellationTokenRegistration 对象。The Register method returns a CancellationTokenRegistration object that is used specifically for this purpose. 以下示例演示了如何使用 Register 方法取消异步 Web 请求。The following example shows how to use the Register method to cancel an asynchronous Web request.

using System;
using System.Net;
using System.Threading;

class Example
{
    static void Main()
    {
        CancellationTokenSource cts = new CancellationTokenSource();

        StartWebRequest(cts.Token);

        // cancellation will cause the web 
        // request to be cancelled
        cts.Cancel();
    }

    static void StartWebRequest(CancellationToken token)
    {
        WebClient wc = new WebClient();
        wc.DownloadStringCompleted += (s, e) => Console.WriteLine("Request completed.");

        // Cancellation on the token will 
        // call CancelAsync on the WebClient.
        token.Register(() =>
        {
            wc.CancelAsync();
            Console.WriteLine("Request cancelled!");
        });

        Console.WriteLine("Starting request.");
        wc.DownloadStringAsync(new Uri("http://www.contoso.com"));
    }
}
Imports System.Net
Imports System.Threading

Class Example
    Private Shared Sub Main()
        Dim cts As New CancellationTokenSource()

        StartWebRequest(cts.Token)

        ' cancellation will cause the web 
        ' request to be cancelled
        cts.Cancel()
    End Sub

    Private Shared Sub StartWebRequest(token As CancellationToken)
        Dim wc As New WebClient()
        wc.DownloadStringCompleted += Function(s, e) Console.WriteLine("Request completed.")

        ' Cancellation on the token will 
        ' call CancelAsync on the WebClient.
        token.Register(Function() 
        wc.CancelAsync()
        Console.WriteLine("Request cancelled!")

End Function)

        Console.WriteLine("Starting request.")
        wc.DownloadStringAsync(New Uri("http://www.contoso.com"))
    End Sub
End Class

CancellationTokenRegistration 对象管理线程同步,并确保回调将在精确的时间点停止执行。The CancellationTokenRegistration object manages thread synchronization and ensures that the callback will stop executing at a precise point in time.

为了确保系统的响应能力并避免死锁,注册回调时必须遵循以下准则:In order to ensure system responsiveness and to avoid deadlocks, the following guidelines must be followed when registering callbacks:

  • 回调方法应该快速,因为它进行同步调用,所以对 Cancel 的调用直到回调返回后才会返回。The callback method should be fast because it is called synchronously and therefore the call to Cancel does not return until the callback returns.

  • 如果回调正在运行时调用 Dispose 且你持有回调正在等待的锁定,则程序可能出现死锁。If you call Dispose while the callback is running, and you hold a lock that the callback is waiting on, your program can deadlock. Dispose 返回后,可释放回调所需的任何资源。After Dispose returns, you can free any resources required by the callback.

  • 回调不应在回调中执行任何手动线程或 SynchronizationContext 使用情况。Callbacks should not perform any manual thread or SynchronizationContext usage in a callback. 如果回调必须在特定线程上运行,请使用 System.Threading.CancellationTokenRegistration 构造函数,此函数使你能够指定目标 syncContext 是活动的 SynchronizationContext.CurrentIf a callback must run on a particular thread, use the System.Threading.CancellationTokenRegistration constructor that enables you to specify that the target syncContext is the active SynchronizationContext.Current. 在回调中执行手动线程处理可能导致死锁。Performing manual threading in a callback can cause deadlock.

有关更完整的示例,请参见如何:注册取消请求的回叫For a more complete example, see How to: Register Callbacks for Cancellation Requests.

通过使用等待句柄进行侦听Listening by Using a Wait Handle

当可取消的操作可在等待同步基元(例如 System.Threading.ManualResetEventSystem.Threading.Semaphore)的同时进行阻止时),可使用 CancellationToken.WaitHandle 属性启用操作同时等待事件请求和取消请求。When a cancelable operation can block while it waits on a synchronization primitive such as a System.Threading.ManualResetEvent or System.Threading.Semaphore, you can use the CancellationToken.WaitHandle property to enable the operation to wait on both the event and the cancellation request. 取消标记的等待句柄将接收到响应取消请求的信号,并且此方法可使用 WaitAny 方法的返回值来确定它是否为发出信号的取消标记。The wait handle of the cancellation token will become signaled in response to a cancellation request, and the method can use the return value of the WaitAny method to determine whether it was the cancellation token that signaled. 然后此操作可根据需要直接退出,或者引发 OperationCanceledExceptionThe operation can then just exit, or throw a OperationCanceledException, as appropriate.

// Wait on the event if it is not signaled.
int eventThatSignaledIndex =
       WaitHandle.WaitAny(new WaitHandle[] { mre, token.WaitHandle },
                          new TimeSpan(0, 0, 20));
' Wait on the event if it is not signaled.
Dim waitHandles() As WaitHandle = { mre, token.WaitHandle }
Dim eventThatSignaledIndex =
    WaitHandle.WaitAny(waitHandles, _
                       New TimeSpan(0, 0, 20))

在面向 .NET Framework 4 的新代码中,System.Threading.ManualResetEventSlimSystem.Threading.SemaphoreSlim 都支持在其 Wait 方法中使用新的取消框架。In new code that targets the .NET Framework 4, System.Threading.ManualResetEventSlim and System.Threading.SemaphoreSlim both support the new cancellation framework in their Wait methods. 可以将 CancellationToken传递给方法,在取消请求发出后,事件就会唤醒并抛出 OperationCanceledExceptionYou can pass the CancellationToken to the method, and when the cancellation is requested, the event wakes up and throws an OperationCanceledException.

try
{
    // mres is a ManualResetEventSlim
    mres.Wait(token);
}
catch (OperationCanceledException)
{
    // Throw immediately to be responsive. The
    // alternative is to do one more item of work,
    // and throw on next iteration, because
    // IsCancellationRequested will be true.
    Console.WriteLine("The wait operation was canceled.");
    throw;
}

Console.Write("Working...");
// Simulating work.
Thread.SpinWait(500000);
Try
   ' mres is a ManualResetEventSlim
    mres.Wait(token)
Catch e As OperationCanceledException
    ' Throw immediately to be responsive. The
    ' alternative is to do one more item of work,
    ' and throw on next iteration, because
    ' IsCancellationRequested will be true.
    Console.WriteLine("Canceled while waiting.")
    Throw
End Try

 ' Simulating work.
Console.Write("Working...")
Thread.SpinWait(500000)

有关更完整的示例,请参见如何:侦听具有等待句柄的取消请求For a more complete example, see How to: Listen for Cancellation Requests That Have Wait Handles.

同时侦听多个标记Listening to Multiple Tokens Simultaneously

在某些情况下,侦听器可能需要同时侦听多个取消标记。In some cases, a listener may have to listen to multiple cancellation tokens simultaneously. 例如,除了在外部作为自变量传递到方法参数的标记以外,可取消操纵可能还必须监视内部取消标记。For example, a cancelable operation may have to monitor an internal cancellation token in addition to a token passed in externally as an argument to a method parameter. 为此,需创建可将两个或多个标记联接成一个标记的链接标记源,如以下示例所示。To accomplish this, create a linked token source that can join two or more tokens into one token, as shown in the following example.

public void DoWork(CancellationToken externalToken)
{
   // Create a new token that combines the internal and external tokens.
   this.internalToken = internalTokenSource.Token;
   this.externalToken = externalToken;

   using (CancellationTokenSource linkedCts =
           CancellationTokenSource.CreateLinkedTokenSource(internalToken, externalToken))
   {
       try {
           DoWorkInternal(linkedCts.Token);
       }
       catch (OperationCanceledException) {
           if (internalToken.IsCancellationRequested) {
               Console.WriteLine("Operation timed out.");
           }
           else if (externalToken.IsCancellationRequested) {
               Console.WriteLine("Cancelling per user request.");
               externalToken.ThrowIfCancellationRequested();
           }
       }
   }
}
Public Sub DoWork(ByVal externalToken As CancellationToken)
   ' Create a new token that combines the internal and external tokens.
   Dim internalToken As CancellationToken = internalTokenSource.Token
   Dim linkedCts As CancellationTokenSource =
   CancellationTokenSource.CreateLinkedTokenSource(internalToken, externalToken)
   Using (linkedCts)
      Try
         DoWorkInternal(linkedCts.Token)
      Catch e As OperationCanceledException
         If e.CancellationToken = internalToken Then
            Console.WriteLine("Operation timed out.")
         ElseIf e.CancellationToken = externalToken Then
            Console.WriteLine("Canceled by external token.")
            externalToken.ThrowIfCancellationRequested()
         End If
      End Try
   End Using
End Sub

请注意,完成后必须在链接标记源上调用 DisposeNotice that you must call Dispose on the linked token source when you are done with it. 有关更完整的示例,请参见如何:侦听多个取消请求For a more complete example, see How to: Listen for Multiple Cancellation Requests.

库代码和用户代码之间的合作Cooperation Between Library Code and User Code

借助统一的取消框架,库代码可取消用户代码,而使用户代码可以协作的方式取消库代码。The unified cancellation framework makes it possible for library code to cancel user code, and for user code to cancel library code in a cooperative manner. 合作是否顺利取决于两者是否遵循以下准则:Smooth cooperation depends on each side following these guidelines:

  • 如果库代码提供了可取消操作,它还应提供接受外部取消标记的公共方法,以便用户代码可以请求取消。If library code provides cancelable operations, it should also provide public methods that accept an external cancellation token so that user code can request cancellation.

  • 如果库代码调用用户代码,库代码应将 OperationCanceledException(externalToken) 解释为协作取消 ,不一定要解释为失败异常。If library code calls into user code, the library code should interpret an OperationCanceledException(externalToken) as cooperative cancellation, and not necessarily as a failure exception.

  • 用户委托应尝试及时响应来自库代码的取消请求。User-delegates should attempt to respond to cancellation requests from library code in a timely manner.

System.Threading.Tasks.TaskSystem.Linq.ParallelEnumerable 是遵循这些准则的类的示例。System.Threading.Tasks.Task and System.Linq.ParallelEnumerable are examples of classes that follow these guidelines. 有关详细信息,请参阅任务取消如何:取消 PLINQ 查询For more information, see Task Cancellation and How to: Cancel a PLINQ Query.

请参阅See also