方法: 待機ハンドルがあるキャンセル要求を待機するHow to: Listen for Cancellation Requests That Have Wait Handles

イベントがシグナル状態になるのを待機している間にメソッドがブロックされた場合、取り消しトークンの値を確認して、適切なタイミングで応答することはできません。If a method is blocked while it is waiting for an event to be signaled, it cannot check the value of the cancellation token and respond in a timely manner. 最初の例は、統合取り消しフレームワークをネイティブにサポートしない System.Threading.ManualResetEvent などのイベントの処理時にこの問題を解決する方法を示しています。The first example shows how to solve this problem when you are working with events such as System.Threading.ManualResetEvent that do not natively support the unified cancellation framework. 2 番目の例は、統合取り消しをサポートする、System.Threading.ManualResetEventSlim を使用するより効率的な方法を示しています。The second example shows a more streamlined approach that uses System.Threading.ManualResetEventSlim, which does support unified cancellation.

注意

[マイ コードのみ] が有効になっている場合、Visual Studio では、例外をスローする行で処理が中断され、"ユーザー コードで処理されない例外" に関するエラー メッセージが表示されることがあります。When "Just My Code" is enabled, Visual Studio in some cases will break on the line that throws the exception and display an error message that says "exception not handled by user code." このエラーは問題にはなりません。This error is benign. F5 キーを押して、処理が中断された箇所から続行し、以下の例に示す例外処理動作を確認できます。You can press F5 to continue from it, and see the exception-handling behavior that is demonstrated in the examples below. Visual Studio による処理が最初のエラーで中断しないようにするには、[ツール] メニューの [オプション]、[デバッグ] 、[全般] の順にクリックし、[マイ コードのみ] チェック ボックスをオフにします。To prevent Visual Studio from breaking on the first error, just uncheck the "Just My Code" checkbox under Tools, Options, Debugging, General.

Example

次の例では ManualResetEvent を使用して、統合取り消しをサポートしない待機ハンドルのブロックを解除する方法を示します。The following example uses a ManualResetEvent to demonstrate how to unblock wait handles that do not support unified cancellation.

using System;
using System.Threading;
using System.Threading.Tasks;

class CancelOldStyleEvents
{
   // Old-style MRE that doesn't support unified cancellation.
   static ManualResetEvent mre = new ManualResetEvent(false);

   static void Main()
   {
      var cts = new CancellationTokenSource();

      // Pass the same token source to the delegate and to the task instance.
      Task.Run(() => DoWork(cts.Token), cts.Token);
      Console.WriteLine("Press s to start/restart, p to pause, or c to cancel.");
      Console.WriteLine("Or any other key to exit.");

      // Old-style UI thread.
      bool goAgain = true;
      while (goAgain) {
         char ch = Console.ReadKey(true).KeyChar;

         switch (ch) {
            case 'c':
               cts.Cancel();
               break;
            case 'p':
               mre.Reset();
               break;
            case 's':
               mre.Set();
               break;
            default:
               goAgain = false;
               break;
         }

         Thread.Sleep(100);
      }
      cts.Dispose();
   }

   static void DoWork(CancellationToken token)
   {
      while (true)
      {
         // Wait on the event if it is not signaled.
         int eventThatSignaledIndex =
                WaitHandle.WaitAny(new WaitHandle[] { mre, token.WaitHandle },
                                   new TimeSpan(0, 0, 20));

         // Were we canceled while waiting?
         if (eventThatSignaledIndex == 1) {
            Console.WriteLine("The wait operation was canceled.");
            throw new OperationCanceledException(token);
         }
         // Were we canceled while running?
         else if (token.IsCancellationRequested) {
            Console.WriteLine("I was canceled while running.");
            token.ThrowIfCancellationRequested();
         }
         // Did we time out?
         else if (eventThatSignaledIndex == WaitHandle.WaitTimeout) {
            Console.WriteLine("I timed out.");
            break;
         }
         else {
            Console.Write("Working... ");
            // Simulating work.
            Thread.SpinWait(5000000);
         }
      }
   }
}
Imports System.Threading
Imports System.Threading.Tasks

Class CancelOldStyleEvents
   ' Old-style MRE that doesn't support unified cancellation.
   Shared mre As New ManualResetEvent(False)

   Shared Sub Main()
      Dim cts As New CancellationTokenSource()

      ' Pass the same token source to the delegate and to the task instance.
      Task.Run(Sub() DoWork(cts.Token), cts.Token)
        Console.WriteLine("Press c to cancel, p to pause, or s to start/restart.")
        Console.WriteLine("Or any other key to exit.")

        ' Old-style UI thread.
        Dim goAgain As Boolean = True
        While goAgain
            Dim ch As Char = Console.ReadKey(True).KeyChar
            Select Case ch
                Case "c"c
                    cts.Cancel()
                Case "p"c
                    mre.Reset()
                Case "s"c
                    mre.Set()
                Case Else
                    goAgain = False
         End Select

         Thread.Sleep(100)
      End While
      cts.Dispose()
   End Sub

    Shared Sub DoWork(ByVal token As CancellationToken)
        While True
            ' 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))

            ' Were we canceled while waiting?
            ' The first If statement is equivalent to
            ' token.ThrowIfCancellationRequested()
            If eventThatSignaledIndex = 1 Then
                Console.WriteLine("The wait operation was canceled.")
                Throw New OperationCanceledException(token)
            ' Were we canceled while running?
            ElseIf token.IsCancellationRequested = True Then
                Console.WriteLine("Cancelling per user request.")
                token.ThrowIfCancellationRequested()
            ' Did we time out?
            ElseIf eventThatSignaledIndex = WaitHandle.WaitTimeout Then
                Console.WriteLine("The wait operation timed out.")
                Exit While
            Else
                ' Simulating work.
                Console.Write("Working... ")
                Thread.SpinWait(5000000)
            End If
        End While
    End Sub
End Class

Example

次の例では ManualResetEventSlim を使用して、統合取り消しをサポートするコーディネーション プリミティブのブロックを解除する方法を示します。The following example uses a ManualResetEventSlim to demonstrate how to unblock coordination primitives that do support unified cancellation. SemaphoreSlimCountdownEvent などの他の軽量なコーディネーション プリミティブでも同じ方法を使用することができます。The same approach can be used with other lightweight coordination primitives, such as SemaphoreSlim and CountdownEvent.

using System;
using System.Threading;
using System.Threading.Tasks;

class CancelNewStyleEvents
{
   // New-style MRESlim that supports unified cancellation
   // in its Wait methods.
   static ManualResetEventSlim mres = new ManualResetEventSlim(false);

   static void Main()
   {
      var cts = new CancellationTokenSource();

      // Pass the same token source to the delegate and to the task instance.
      Task.Run(() => DoWork(cts.Token), cts.Token);
      Console.WriteLine("Press c to cancel, p to pause, or s to start/restart,");
      Console.WriteLine("or any other key to exit.");

      // New-style UI thread.
         bool goAgain = true;
         while (goAgain)
         {
             char ch = Console.ReadKey(true).KeyChar;

             switch (ch)
             {
                 case 'c':
                     // Token can only be canceled once.
                     cts.Cancel();
                     break;
                 case 'p':
                     mres.Reset();
                     break;
                 case 's':
                     mres.Set();
                     break;
                 default:
                     goAgain = false;
                     break;
             }

             Thread.Sleep(100);
         }
         cts.Dispose();
     }

     static void DoWork(CancellationToken token)
     {

         while (true)
         {
             if (token.IsCancellationRequested)
             {
                 Console.WriteLine("Canceled while running.");
                 token.ThrowIfCancellationRequested();
             }

             // Wait on the event to be signaled
             // or the token to be canceled,
             // whichever comes first. The token
             // will throw an exception if it is canceled
             // while the thread is waiting on the event.
             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);
         }
     }
 }
Imports System.Threading
Imports System.Threading.Tasks

Class CancelNewStyleEvents

    ' New-style MRESlim that supports unified cancellation
    ' in its Wait methods.
    Shared mres As ManualResetEventSlim = New ManualResetEventSlim(False)

    Shared Sub Main()

        Dim cts As New CancellationTokenSource()

        ' Pass the same token source to the delegate and to the task instance.
      Task.Run(Sub() DoWork(cts.Token), cts.Token)
        Console.WriteLine("Press c to cancel, p to pause, or s to start/restart,")
        Console.WriteLine("or any other key to exit.")

        ' New-style UI thread.
        Dim goAgain As Boolean = True
        While goAgain = True

            Dim ch As Char = Console.ReadKey(True).KeyChar

            Select Case ch
                Case "c"c
                    ' Token can only be canceled once.
                    cts.Cancel()
                Case "p"c
                    mres.Reset()
                Case "s"c
                    mres.Set()
                Case Else
                    goAgain = False
            End Select

            Thread.Sleep(100)
        End While
        cts.Dispose()
    End Sub

    Shared Sub DoWork(ByVal token As CancellationToken)
        While True
            If token.IsCancellationRequested Then
                Console.WriteLine("Canceled while running.")
                token.ThrowIfCancellationRequested()
            End If

            ' Wait on the event to be signaled
            ' or the token to be canceled,
            ' whichever comes first. The token
            ' will throw an exception if it is canceled
            ' while the thread is waiting on the event.
            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)
        End While
    End Sub
End Class

関連項目See also