Vorgehensweise: Lauschen auf Abbruchanforderungen durch Abruf

Das folgende Beispiel zeigt eine Möglichkeit, wie Benutzercode ein Abbruchtoken in regelmäßigen Abständen abfragen kann, um festzustellen, ob vom aufrufenden Thread ein Abbruch angefordert wurde. In diesem Beispiel wird der System.Threading.Tasks.Task-Typ verwendet, aber das gleiche Muster gilt für asynchrone Vorgänge, die direkt vom System.Threading.ThreadPool- oder System.Threading.Thread-Typ erstellt werden.

Beispiel

Abrufen erfordert eine Schleife oder rekursiven Code, der den Wert der booleschen IsCancellationRequested-Eigenschaft in regelmäßigen Abständen lesen kann. Bei Verwendung des System.Threading.Tasks.Task-Typs und Warten auf den Abschluss der Aufgabe im aufrufenden Thread können Sie mithilfe der ThrowIfCancellationRequested-Methode die Eigenschaft überprüfen und die Ausnahme auslösen. Mithilfe dieser Methode stellen Sie sicher, dass die richtige Ausnahme als Reaktion auf eine Anforderung ausgelöst wird. Bei Verwendung eines Task ist das Aufrufen dieser Methode besser als das manuelle Auslösen einer OperationCanceledException. Wenn Sie keine Ausnahme auslösen müssen, reicht es aus, wenn Sie einfach die Eigenschaft überprüfen und aus der Methode zurückkehren, wenn die Eigenschaft true ist.

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 col = 0; col < rect.columns && !token.IsCancellationRequested; col++) {
         // Assume that we know that the inner loop is very fast.
         // Therefore, polling once per column in the outer loop condition
         // is sufficient.
         for (int row = 0; row < rect.rows; row++) {
            // Simulating work.
            Thread.SpinWait(5_000);
            Console.Write("{0},{1} ", col, row);
         }
      }

      if (token.IsCancellationRequested) {
         // Cleanup or undo here if necessary...
         Console.WriteLine("\r\nOperation canceled");
         Console.WriteLine("Press any key to exit.");

         // 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)
        Dim col As Integer
        For col = 0 To rect.columns - 1
            ' Assume that we know that the inner loop is very fast.
            ' Therefore, polling once per column in the outer loop condition
            ' is sufficient.
            For col As Integer = 0 To rect.rows - 1
                ' Simulating work.
                Thread.SpinWait(5000)
                Console.Write("0',1' ", x, y)
            Next
        Next

        If token.IsCancellationRequested = True Then
            ' Cleanup or undo here if necessary...
            Console.WriteLine(vbCrLf + "Operation canceled")
            Console.WriteLine("Press any key to exit.")

            ' If using Task:
            ' token.ThrowIfCancellationRequested()
        End If
    End Sub
End Class

Das Aufrufen von ThrowIfCancellationRequested ist äußerst schnell und nicht mit bedeutendem Aufwand in Schleifen verbunden.

Beim Aufrufen von ThrowIfCancellationRequested müssen Sie die IsCancellationRequested-Eigenschaft nur dann explizit überprüfen, wenn als Reaktion auf den Abbruch neben dem Auslösen der Ausnahme noch Anderes erforderlich ist. In diesem Beispiel sehen Sie, dass der Code tatsächlich zweimal auf die Eigenschaft zugreift: einmal mit explizitem Zugriff und erneut in der ThrowIfCancellationRequested-Methode. Da jedoch das Lesen der IsCancellationRequested-Eigenschaft nur eine flüchtige Leseanweisung pro Zugriff beinhaltet, ist der doppelte Zugriff im Hinblick auf die Leistung nicht signifikant. Dennoch ist der Methodenaufruf dem manuellen Auslösen der OperationCanceledException vorzuziehen.

Siehe auch