マネージド スレッドのキャンセル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. 新しいスレッドまたは新しいタスクの作成などによって 1 つ以上のキャンセル可能な操作を呼び出すオブジェクトは、各操作にトークンを渡します。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 オブジェクトのインスタンスを作成します。このオブジェクトでは、個々のキャンセル トークンへのキャンセル通知を管理し、送信します。Instantiate a CancellationTokenSource object, which manages and sends cancellation notification to the individual cancellation tokens.

  • CancellationTokenSource.Token プロパティによって返されるトークンを、キャンセルをリッスンしているそれぞれのタスクまたはスレッドに渡します。Pass the token returned by the CancellationTokenSource.Token property to each task or thread that listens for cancellation.

  • それぞれのタスクまたはスレッドに対し、キャンセルに応答するメカニズムを提供します。Provide a mechanism for each task or thread to respond to cancellation.

  • キャンセルの通知を提供する CancellationTokenSource.Cancel メソッドを呼び出します。Call the CancellationTokenSource.Cancel method to provide notification of cancellation.

重要

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 とキャンセル トークン 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.

  • 要求側のオブジェクトは、1 回のメソッド呼び出しでトークンのすべてのコピーにキャンセル要求を発行できます。The requesting object issues the cancellation request to all copies of the token by using just one method call.

  • リスナーは、複数のトークンを 1 つのリンク トークンに結合して同時にリッスンできます。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 1 つ以上のリスナーに渡される軽量な値型で、通常はメソッド パラメーターとして渡されます。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.ParallelEnumerable です。The 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.

注意

この例では、新しいキャンセル フレームワークが従来の API と互換性があることを示すために、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. 1 つのキャンセル トークンは 1 つの "キャンセル可能な操作" を参照している必要がありますが、その操作がプログラムに実装されている場合があります。One cancellation token should refer to one "cancelable operation," however that operation may be implemented in your program. トークンの IsCancellationRequested プロパティを true に設定した後に、false に再設定することはできません。After 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. こうすることで、他の操作には影響を与えずに 1 つの操作を取り消すことができます。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 メソッドを呼び出すことです。これにより、OperationCanceledException がスローされます。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 クラスはこの方法で OperationCanceledException を処理します。The 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. ポーリングを行う方法の 1 つの例を次に示します。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.Current であることを指定できます。If 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. 操作はその後、状況に合わせて、そのまま終了するか、OperationCanceledException をスローします。The 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 を渡すことができ、キャンセルが要求されると、イベントが起動して OperationCanceledException がスローされます。You 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. これを行うには、複数のトークンを 1 つのトークンに結合できるリンク トークン ソースを作成します。次に例を示します。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

処理の終了後にリンク トークン ソースに対して Dispose を呼び出す必要があることに注意してください。Notice 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.Task および System.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