Managed 執行緒中的取消作業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. 當您完成使用取消權杖來釋放任何它所保留的 Unmanaged 資源之後,您一定要呼叫 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 和解除標記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.

  • 接聽程式可以透過將這些權杖聯結成單一個「連結的權杖」(linked token) 來同時接聽多個權杖。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 方法來示範新的取消架構與舊版應用程式開發介面相容。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. 在這種情況下,終止作業的正確方式是委派要呼叫會導致擲回 OperationCanceledExceptionThrowIfCancellationRequested 方法。In 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