方法: ポーリングによりキャンセル要求を待機するHow to: Listen for Cancellation Requests by Polling

次の例は、ユーザー コードで取り消しトークンを定期的にポーリングし、呼び出し元のスレッドから取り消しが要求されているかどうかを確認する 1 つの方法を示しています。The following example shows one way that user code can poll a cancellation token at regular intervals to see whether cancellation has been requested from the calling thread. この例では System.Threading.Tasks.Task 型を使用しますが、System.Threading.ThreadPool 型または System.Threading.Thread 型で直接作成される非同期操作にも同じパターンが適用されます。This example uses the System.Threading.Tasks.Task type, but the same pattern applies to asynchronous operations created directly by the System.Threading.ThreadPool type or the System.Threading.Thread type.

Example

ポーリングには、ブール IsCancellationRequested プロパティの値を定期的に読み取ることができる、ある種のループまたは再帰的なコードが必要になります。Polling requires some kind of loop or recursive code that can periodically read the value of the Boolean IsCancellationRequested property. System.Threading.Tasks.Task 型を使用し、タスクが呼び出し元スレッドで完了するまで待機する場合は、ThrowIfCancellationRequested スレッドを使用してプロパティを確認し、例外をスローすることができます。If you are using the System.Threading.Tasks.Task type and you are waiting for the task to complete on the calling thread, you can use the ThrowIfCancellationRequested method to check the property and throw the exception. このメソッドを使用することで、要求に応じて、正しい例外がスローされるようになります。By using this method, you ensure that the correct exception is thrown in response to a request. Task を使用する場合は、OperationCanceledException を手動でスローするよりもこのスレッドを呼び出す方が効果的です。If you are using a Task, then calling this method is better than manually throwing an OperationCanceledException. 例外をスローする必要がない場合は、単にプロパティを確認し、プロパティが true である場合はメソッドから制御を返すことができます。If you do not have to throw the exception, then you can just check the property and return from the method if the property is true.

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

public struct Rectangle
{
   public int columns;
   public int rows;
}

class CancelByPolling
{
   static void Main()
   {
      var tokenSource = new CancellationTokenSource();
      // Toy object for demo purposes
      Rectangle rect = new Rectangle() { columns = 1000, rows = 500 };

      // Simple cancellation scenario #1. Calling thread does not wait
      // on the task to complete, and the user delegate simply returns
      // on cancellation request without throwing.
      Task.Run(() => NestedLoops(rect, tokenSource.Token), tokenSource.Token);

      // Simple cancellation scenario #2. Calling thread does not wait
      // on the task to complete, and the user delegate throws
      // OperationCanceledException to shut down task and transition its state.
      // Task.Run(() => PollByTimeSpan(tokenSource.Token), tokenSource.Token);

      Console.WriteLine("Press 'c' to cancel");
      if (Console.ReadKey(true).KeyChar == 'c') {
          tokenSource.Cancel();
          Console.WriteLine("Press any key to exit.");
      }

      Console.ReadKey();
      tokenSource.Dispose();
  }

   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();
         }
      }
   }
}
Imports System.Threading
Imports System.Threading.Tasks

Public Structure Rectangle
    Public columns As Integer
    Public rows As Integer
End Structure

Class CancelByPolling
    Shared Sub Main()
        Dim tokenSource As New CancellationTokenSource()
        ' Toy object for demo purposes
        Dim rect As New Rectangle()
        rect.columns = 1000
        rect.rows = 500

        ' Simple cancellation scenario #1. Calling thread does not wait
        ' on the task to complete, and the user delegate simply returns
        ' on cancellation request without throwing.
        Task.Run(Sub() NestedLoops(rect, tokenSource.Token), tokenSource.Token)

        ' Simple cancellation scenario #2. Calling thread does not wait
        ' on the task to complete, and the user delegate throws 
        ' OperationCanceledException to shut down task and transition its state.
        ' Task.Run(Sub() PollByTimeSpan(tokenSource.Token), tokenSource.Token)

        Console.WriteLine("Press 'c' to cancel")
        If Console.ReadKey(True).KeyChar = "c"c Then

            tokenSource.Cancel()
            Console.WriteLine("Press any key to exit.")
        End If

        Console.ReadKey()
        tokenSource.Dispose()
    End Sub

    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
End Class

ThrowIfCancellationRequested の呼び出しは非常に高速で、ループで大きなオーバーヘッドが発生することはありません。Calling ThrowIfCancellationRequested is extremely fast and does not introduce significant overhead in loops.

ThrowIfCancellationRequested を呼び出す場合に、例外をスローするだけではなく、取り消しに応じて他の作業を行う場合は IsCancellationRequested プロパティを明示的に確認するだけで済みます。If you are calling ThrowIfCancellationRequested, you only have to explicitly check the IsCancellationRequested property if you have other work to do in response to the cancellation besides throwing the exception. この例では、コードで実際にプロパティに 2 回アクセスするのがわかります。つまり、明示的なアクセスで 1 回、ThrowIfCancellationRequested メソッドでもう 1 回です。In this example, you can see that the code actually accesses the property twice: once in the explicit access and again in the ThrowIfCancellationRequested method. ただし、IsCancellationRequested プロパティの読み取り操作ではアクセスごとに 1 つの volatile 読み取りのみが含まれるため、パフォーマンスの観点からは二重アクセスは重要ではありません。But because the act of reading the IsCancellationRequested property involves only one volatile read instruction per access, the double access is not significant from a performance perspective. それでも、OperationCanceledException を手動でスローするよりメソッドを呼び出すことをお勧めします。It is still preferable to call the method rather than manually throw the OperationCanceledException.

関連項目See also