Sdílet prostřednictvím


Postupy: Naslouchání požadavkům zrušení dotazováním

Následující příklad ukazuje jeden ze způsobů, jak může uživatelský kód dotazovat token zrušení v pravidelných intervalech, aby bylo možné zjistit, jestli bylo zrušení požadováno z volajícího vlákna. Tento příklad používá System.Threading.Tasks.Task typ, ale stejný vzor platí pro asynchronní operace vytvořené přímo typem System.Threading.ThreadPool nebo typem System.Threading.Thread .

Příklad

Dotazování vyžaduje určitý druh smyčky nebo rekurzivní kód, který může pravidelně číst hodnotu logické IsCancellationRequested vlastnosti. Pokud používáte System.Threading.Tasks.Task typ a čekáte na dokončení úlohy ve volajícím vlákně, můžete pomocí ThrowIfCancellationRequested metody zkontrolovat vlastnost a vyvolat výjimku. Pomocí této metody se ujistěte, že je v reakci na požadavek vyvolán správná výjimka. Pokud používáte Task, volání této metody je lepší než ruční vyvolání OperationCanceledException. Pokud není nutné vyvolat výjimku, můžete pouze zkontrolovat vlastnost a vrátit z metody, pokud je truevlastnost .

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 Main11()
        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 row As Integer = 0 To rect.rows - 1
                ' Simulating work.
                Thread.SpinWait(5000)
                Console.Write("0',1' ", col, row)
            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

Volání ThrowIfCancellationRequested je extrémně rychlé a nezavádí významné režijní náklady ve smyčce.

Pokud voláte ThrowIfCancellationRequested, musíte vlastnost explicitně zkontrolovat IsCancellationRequested pouze v případě, že máte jinou práci v reakci na zrušení kromě vyvolání výjimky. V tomto příkladu vidíte, že kód ve skutečnosti přistupuje k vlastnosti dvakrát: jednou v explicitním přístupu a znovu v ThrowIfCancellationRequested metodě. Vzhledem k tomu, že operace čtení IsCancellationRequested vlastnosti zahrnuje pouze jednu nestálou instrukci čtení na přístup, dvojitý přístup není z hlediska výkonu významný. Je stále vhodnější volat metodu spíše než ručně vyvolat OperationCanceledException.

Viz také