Dela via


Gör så här: Lyssna efter avbokningsbegäranden genom avsökning

I följande exempel visas ett sätt att använda kod kan avsöka en annulleringstoken med jämna mellanrum för att se om annullering har begärts från den anropande tråden. I det System.Threading.Tasks.Task här exemplet används typen, men samma mönster gäller för asynkrona åtgärder som skapas direkt av System.Threading.ThreadPool typen eller System.Threading.Thread typen.

Exempel

Avsökning kräver någon form av loop eller rekursiv kod som regelbundet kan läsa värdet för den booleska IsCancellationRequested egenskapen. Om du använder System.Threading.Tasks.Task typen och väntar på att uppgiften ska slutföras i den anropande tråden ThrowIfCancellationRequested kan du använda metoden för att kontrollera egenskapen och utlösa undantaget. Med den här metoden ser du till att rätt undantag utlöses som svar på en begäran. Om du använder en Taskär det bättre att anropa den här metoden än att manuellt kasta en OperationCanceledException. Om du inte behöver utlösa undantaget kan du bara kontrollera egenskapen och returnera från -metoden om egenskapen är 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 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

Anrop ThrowIfCancellationRequested är extremt snabbt och medför inte betydande omkostnader i loopar.

Om du anropar ThrowIfCancellationRequestedbehöver du bara uttryckligen IsCancellationRequested kontrollera egenskapen om du har annat arbete att göra som svar på annulleringen förutom att utlösa undantaget. I det här exemplet kan du se att koden faktiskt kommer åt egenskapen två gånger: en gång i den explicita åtkomsten ThrowIfCancellationRequested och igen i metoden. Men eftersom åtgärden att läsa IsCancellationRequested egenskapen endast omfattar en flyktig läsinstruktion per åtkomst, är den dubbla åtkomsten inte betydande ur ett prestandaperspektiv. Det är fortfarande bättre att anropa metoden i stället för att manuellt kasta OperationCanceledException.

Se även