Anulowanie w zarządzanych wątkachCancellation in Managed Threads

Począwszy od .NET Framework 4, .NET Framework korzysta z ujednoliconego modelu do obsługi operacji synchronicznych asynchronicznych lub długotrwałych.Starting with the .NET Framework 4, the .NET Framework uses a unified model for cooperative cancellation of asynchronous or long-running synchronous operations. Ten model jest oparty na obiekcie lekkim nazywanym tokenem anulowania.This model is based on a lightweight object called a cancellation token. Obiekt, który wywołuje jedną lub kilka operacji do anulowania, na przykład przez utworzenie nowych wątków lub zadań, przekazuje token do każdej operacji.The object that invokes one or more cancelable operations, for example by creating new threads or tasks, passes the token to each operation. Poszczególne operacje mogą z kolei przekazywać kopie tokenu do innych operacji.Individual operations can in turn pass copies of the token to other operations. W późniejszym czasie obiekt, który utworzył token, może go użyć do żądania zatrzymywania wykonywania operacji.At some later time, the object that created the token can use it to request that the operations stop what they are doing. Tylko obiekt żądający może wydać żądanie anulowania, a każdy odbiornik jest odpowiedzialny za obserwowanie żądania i odpowiadanie na nie w odpowiednim czasie.Only the requesting object can issue the cancellation request, and each listener is responsible for noticing the request and responding to it in an appropriate and timely manner.

Ogólny wzorzec dla implementacji spółdzielczego modelu anulowania to:The general pattern for implementing the cooperative cancellation model is:

  • Utwórz wystąpienie CancellationTokenSource obiektu, który zarządza i wysyła powiadomienie o anulowaniu do poszczególnych tokenów anulowania.Instantiate a CancellationTokenSource object, which manages and sends cancellation notification to the individual cancellation tokens.

  • Przekaż token zwracany przez właściwość CancellationTokenSource.Token do każdego zadania lub wątku, który nasłuchuje w celu anulowania.Pass the token returned by the CancellationTokenSource.Token property to each task or thread that listens for cancellation.

  • Udostępnij mechanizm dla każdego zadania lub wątku, aby odpowiedzieć na anulowanie.Provide a mechanism for each task or thread to respond to cancellation.

  • Wywołaj metodę CancellationTokenSource.Cancel, aby powiadomić o anulowaniu.Call the CancellationTokenSource.Cancel method to provide notification of cancellation.

Ważne

Klasa CancellationTokenSource implementuje interfejs IDisposable.The CancellationTokenSource class implements the IDisposable interface. Należy pamiętać, aby wywołać metodę CancellationTokenSource.Dispose po zakończeniu korzystania ze źródła tokenu anulowania w celu zwolnienia wszelkich niezarządzanych zasobów, które przechowuje.You should be sure to call the CancellationTokenSource.Dispose method when you have finished using the cancellation token source to free any unmanaged resources it holds.

Poniższa ilustracja przedstawia relację między źródłem tokenu i wszystkimi kopiami jego tokenu.The following illustration shows the relationship between a token source and all the copies of its token.

Tokeny CancellationTokenSource i anulowaniaCancellationTokenSource and cancellation tokens

Nowy model anulowania ułatwia tworzenie aplikacji obsługujących anulowanie i bibliotek oraz obsługę następujących funkcji:The new cancellation model makes it easier to create cancellation-aware applications and libraries, and it supports the following features:

  • Anulowanie jest wspólne i nie jest wymuszane na odbiorniku.Cancellation is cooperative and is not forced on the listener. Odbiornik Określa, jak bezpiecznie kończyć się w odpowiedzi na żądanie anulowania.The listener determines how to gracefully terminate in response to a cancellation request.

  • Żądanie jest różne od nasłuchiwania.Requesting is distinct from listening. Obiekt, który wywołuje operację do anulowania, może kontrolować, kiedy jest wymagane anulowanie (Jeśli kiedykolwiek).An object that invokes a cancelable operation can control when (if ever) cancellation is requested.

  • Obiekt żądający wysyła żądanie anulowania do wszystkich kopii tokenu przy użyciu tylko jednego wywołania metody.The requesting object issues the cancellation request to all copies of the token by using just one method call.

  • Odbiornik może nasłuchiwać wielu tokenów jednocześnie, łącząc je w jeden połączony token.A listener can listen to multiple tokens simultaneously by joining them into one linked token.

  • Kod użytkownika może zauważyć i odpowiadać na żądania anulowania z kodu biblioteki, a kod biblioteki może zauważyć żądania anulowania od kodu użytkownika i odpowiadać na nie.User code can notice and respond to cancellation requests from library code, and library code can notice and respond to cancellation requests from user code.

  • Odbiorniki mogą otrzymywać powiadomienia o żądaniach anulowania przez sondowanie, rejestrację wywołania zwrotnego lub oczekiwanie na uchwyty oczekiwania.Listeners can be notified of cancellation requests by polling, callback registration, or waiting on wait handles.

Typy anulowaniaCancellation Types

Platforma anulowania jest zaimplementowana jako zestaw powiązanych typów, które są wymienione w poniższej tabeli.The cancellation framework is implemented as a set of related types, which are listed in the following table.

Nazwa typuType name OpisDescription
CancellationTokenSource Obiekt, który tworzy token anulowania, a także wystawia żądanie anulowania dla wszystkich kopii tego tokenu.Object that creates a cancellation token, and also issues the cancellation request for all copies of that token.
CancellationToken Typ wartości uproszczonej przesłany do jednego lub większej liczby detektorów, zazwyczaj jako parametr metody.Lightweight value type passed to one or more listeners, typically as a method parameter. Odbiorniki monitorują wartość właściwości IsCancellationRequested tokenu przez sondowanie, wywołanie zwrotne lub dojście oczekiwania.Listeners monitor the value of the IsCancellationRequested property of the token by polling, callback, or wait handle.
OperationCanceledException Przeciążenia konstruktora tego wyjątku akceptują CancellationToken jako parametr.Overloads of this exception's constructor accept a CancellationToken as a parameter. Odbiorniki mogą opcjonalnie zgłosić ten wyjątek, aby zweryfikować Źródło anulowania i poinformować inne, że odpowiedział na żądanie anulowania.Listeners can optionally throw this exception to verify the source of the cancellation and notify others that it has responded to a cancellation request.

Nowy model anulowania jest zintegrowany z .NET Framework w kilku typach.The new cancellation model is integrated into the .NET Framework in several types. Najważniejsze są System.Threading.Tasks.Parallel, System.Threading.Tasks.Task, System.Threading.Tasks.Task<TResult> i System.Linq.ParallelEnumerable.The most important ones are System.Threading.Tasks.Parallel, System.Threading.Tasks.Task, System.Threading.Tasks.Task<TResult> and System.Linq.ParallelEnumerable. Zalecamy używanie tego nowego modelu anulowania dla całej nowej biblioteki i kodu aplikacji.We recommend that you use this new cancellation model for all new library and application code.

Przykład koduCode Example

W poniższym przykładzie obiekt żądający tworzy obiekt CancellationTokenSource, a następnie przekazuje jego właściwość Token do operacji do anulowania.In the following example, the requesting object creates a CancellationTokenSource object, and then passes its Token property to the cancelable operation. Operacja, która odbiera żądanie, monitoruje wartość właściwości IsCancellationRequested tokenu przez sondowanie.The operation that receives the request monitors the value of the IsCancellationRequested property of the token by polling. Gdy wartość stanie się true, odbiornik może zakończyć się w dowolny sposób.When the value becomes true, the listener can terminate in whatever manner is appropriate. W tym przykładzie metoda tylko opuszcza, która jest wymagana w wielu przypadkach.In this example, the method just exits, which is all that is required in many cases.

Uwaga

W przykładzie zastosowano metodę QueueUserWorkItem, aby zademonstrować, że nowa struktura anulowania jest zgodna ze starszymi interfejsami API.The example uses the QueueUserWorkItem method to demonstrate that the new cancellation framework is compatible with legacy APIs. Aby zapoznać się z przykładem korzystającym z nowego, preferowanego typu System.Threading.Tasks.Task, zobacz How to: Cancel a Task and jego Children.For an example that uses the new, preferred System.Threading.Tasks.Task type, see How to: Cancel a Task and Its Children.

using System;
using System.Threading;

public class Example
{
   public static void Main()
   {
      // Create the token source.
      CancellationTokenSource cts = new CancellationTokenSource();

      // Pass the token to the cancelable operation.
      ThreadPool.QueueUserWorkItem(new WaitCallback(DoSomeWork), cts.Token);
      Thread.Sleep(2500);

      // Request cancellation.
      cts.Cancel();
      Console.WriteLine("Cancellation set in token source...");
      Thread.Sleep(2500);
      // Cancellation should have happened, so call Dispose.
      cts.Dispose();
   }

   // Thread 2: The listener
   static void DoSomeWork(object obj)
   {
      CancellationToken token = (CancellationToken)obj;

      for (int i = 0; i < 100000; i++) {
         if (token.IsCancellationRequested)
         {
            Console.WriteLine("In iteration {0}, cancellation has been requested...",
                              i + 1);
            // Perform cleanup if necessary.
            //...
            // Terminate the operation.
            break;
         }
         // Simulate some work.
         Thread.SpinWait(500000);
      }
   }
}
// The example displays output like the following:
//       Cancellation set in token source...
//       In iteration 1430, cancellation has been requested...
Imports System.Threading

Module Example
   Public Sub Main()
      ' Create the token source.
      Dim cts As New CancellationTokenSource()

      ' Pass the token to the cancelable operation.
      ThreadPool.QueueUserWorkItem(New WaitCallback(AddressOf DoSomeWork), cts.Token)
      Thread.Sleep(2500)
        
      ' Request cancellation by setting a flag on the token.
      cts.Cancel()
      Console.WriteLine("Cancellation set in token source...")
      Thread.Sleep(2500)
      ' Cancellation should have happened, so call Dispose.
      cts.Dispose()
   End Sub

   ' Thread 2: The listener
   Sub DoSomeWork(ByVal obj As Object)
      Dim token As CancellationToken = CType(obj, CancellationToken)
      
      For i As Integer = 0 To 1000000
         If token.IsCancellationRequested Then
            Console.WriteLine("In iteration {0}, cancellation has been requested...",
                              i + 1)
            ' Perform cleanup if necessary.
            '...
            ' Terminate the operation.
            Exit For
         End If
      
         ' Simulate some work.
         Thread.SpinWait(500000)
      Next
   End Sub
End Module
' The example displays output like the following:
'       Cancellation set in token source...
'       In iteration 1430, cancellation has been requested...

Anulowanie operacji względem anulowania obiektuOperation Cancellation Versus Object Cancellation

W nowej strukturze anulowania odwołanie odwołuje się do operacji, a nie obiektów.In the new cancellation framework, cancellation refers to operations, not objects. Żądanie anulowania oznacza, że operacja powinna zostać zatrzymana tak szybko, jak to możliwe po przeprowadzeniu wymaganego oczyszczenia.The cancellation request means that the operation should stop as soon as possible after any required cleanup is performed. Jeden token anulowania powinien odwoływać się do jednej "operacji możliwej do anulowania", jednak może być zaimplementowana w programie.One cancellation token should refer to one "cancelable operation," however that operation may be implemented in your program. Po ustawieniu właściwości IsCancellationRequested tokenu na truenie można resetować do false.After the IsCancellationRequested property of the token has been set to true, it cannot be reset to false. W związku z tym tokeny anulowania nie mogą być ponownie używane po anulowaniu.Therefore, cancellation tokens cannot be reused after they have been canceled.

Jeśli wymagany jest mechanizm anulowania obiektu, można oprzeć go na mechanizmie anulowania operacji, wywołując metodę CancellationToken.Register, jak pokazano w poniższym przykładzie.If you require an object cancellation mechanism, you can base it on the operation cancellation mechanism by calling the CancellationToken.Register method, as shown in the following example.

using System;
using System.Threading;

class CancelableObject
{
   public string id;
   
   public CancelableObject(string id)
   {
      this.id = id;
   }
   
   public void Cancel() 
   { 
      Console.WriteLine("Object {0} Cancel callback", id);
      // Perform object cancellation here.
   }
}

public class Example
{
   public static void Main()
   {
      CancellationTokenSource cts = new CancellationTokenSource();
      CancellationToken token = cts.Token;

      // User defined Class with its own method for cancellation
      var obj1 = new CancelableObject("1");
      var obj2 = new CancelableObject("2");
      var obj3 = new CancelableObject("3");

      // Register the object's cancel method with the token's
      // cancellation request.
      token.Register(() => obj1.Cancel());
      token.Register(() => obj2.Cancel());
      token.Register(() => obj3.Cancel());

      // Request cancellation on the token.
      cts.Cancel();
      // Call Dispose when we're done with the CancellationTokenSource.
      cts.Dispose();
   }
}
// The example displays the following output:
//       Object 3 Cancel callback
//       Object 2 Cancel callback
//       Object 1 Cancel callback
Imports System.Threading

Class CancelableObject
   Public id As String
   
   Public Sub New(id As String)
      Me.id = id
   End Sub
   
   Public Sub Cancel() 
      Console.WriteLine("Object {0} Cancel callback", id)
      ' Perform object cancellation here.
   End Sub
End Class

Module Example
   Public Sub Main()
        Dim cts As New CancellationTokenSource()
        Dim token As CancellationToken = cts.Token

        ' User defined Class with its own method for cancellation
        Dim obj1 As New CancelableObject("1")
        Dim obj2 As New CancelableObject("2")
        Dim obj3 As New CancelableObject("3")

        ' Register the object's cancel method with the token's
        ' cancellation request.
        token.Register(Sub() obj1.Cancel())
        token.Register(Sub() obj2.Cancel())
        token.Register(Sub() obj3.Cancel())

        ' Request cancellation on the token.
        cts.Cancel()
        ' Call Dispose when we're done with the CancellationTokenSource.
        cts.Dispose()
   End Sub
End Module
' The example displays output like the following:
'       Object 3 Cancel callback
'       Object 2 Cancel callback
'       Object 1 Cancel callback

Jeśli obiekt obsługuje więcej niż jedną współbieżną operację, należy przekazać oddzielny token jako dane wejściowe do każdej unikatowej operacji anulowania.If an object supports more than one concurrent cancelable operation, pass a separate token as input to each distinct cancelable operation. Dzięki temu można anulować jedną operację bez wpływu na inne osoby.That way, one operation can be cancelled without affecting the others.

Nasłuchiwanie żądań anulowania i reagowanie na nieListening and Responding to Cancellation Requests

W delegatze użytkownika, realizator operacji anulowania określa sposób kończenia operacji w odpowiedzi na żądanie anulowania.In the user delegate, the implementer of a cancelable operation determines how to terminate the operation in response to a cancellation request. W wielu przypadkach delegat użytkownika może po prostu wykonać wymagane czyszczenie, a następnie natychmiast zwrócić.In many cases, the user delegate can just perform any required cleanup and then return immediately.

Jednak w bardziej złożonych przypadkach może być konieczne, aby delegat użytkownika powiadamiał kod biblioteki, że wystąpiło anulowanie.However, in more complex cases, it might be necessary for the user delegate to notify library code that cancellation has occurred. W takich przypadkach prawidłowym sposobem zakończenia operacji jest, aby delegat wywołał ThrowIfCancellationRequested, metodę, która spowoduje zgłoszenie OperationCanceledException.In such cases, the correct way to terminate the operation is for the delegate to call the ThrowIfCancellationRequested, method, which will cause an OperationCanceledException to be thrown. Kod biblioteki może przechwytywać ten wyjątek w wątku delegata użytkownika i sprawdzać token wyjątku, aby określić, czy wyjątek wskazuje na wcześniejsze anulowanie lub inne wyjątkowe sytuacje.Library code can catch this exception on the user delegate thread and examine the exception's token to determine whether the exception indicates cooperative cancellation or some other exceptional situation.

Klasa Task obsługuje OperationCanceledException w ten sposób.The Task class handles OperationCanceledException in this way. Aby uzyskać więcej informacji, zobacz Anulowanie zadania.For more information, see Task Cancellation.

Nasłuchiwanie przez sondowanieListening by Polling

W przypadku długotrwałych obliczeń, które są wykonywane w pętli lub rekursywnie, można nasłuchiwać żądania anulowania przez okresowe sondowanie wartości właściwości CancellationToken.IsCancellationRequested.For long-running computations that loop or recurse, you can listen for a cancellation request by periodically polling the value of the CancellationToken.IsCancellationRequested property. Jeśli wartość jest true, metoda powinna czyścić i kończyć się tak szybko, jak to możliwe.If its value is true, the method should clean up and terminate as quickly as possible. Optymalna częstotliwość sondowania zależy od typu aplikacji.The optimal frequency of polling depends on the type of application. Deweloper może określić najlepszą częstotliwość sondowania dla danego programu.It is up to the developer to determine the best polling frequency for any given program. Sondowanie nie ma znaczącego wpływu na wydajność.Polling itself does not significantly impact performance. Poniższy przykład pokazuje jeden możliwy sposób sondowania.The following example shows one possible way to poll.

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();
      }
   }
}
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

Aby zapoznać się z bardziej kompletnym przykładem, zobacz jak: nasłuchiwanie żądań anulowania przez sondowanie.For a more complete example, see How to: Listen for Cancellation Requests by Polling.

Nasłuchiwanie przez zarejestrowanie wywołania zwrotnegoListening by Registering a Callback

Niektóre operacje mogą zostać zablokowane w taki sposób, że nie mogą sprawdzić wartości tokenu anulowania w odpowiednim czasie.Some operations can become blocked in such a way that they cannot check the value of the cancellation token in a timely manner. W takich przypadkach można zarejestrować metodę wywołania zwrotnego, która odblokowuje metodę po odebraniu żądania anulowania.For these cases, you can register a callback method that unblocks the method when a cancellation request is received.

Metoda Register zwraca obiekt CancellationTokenRegistration, który jest używany specjalnie do tego celu.The Register method returns a CancellationTokenRegistration object that is used specifically for this purpose. W poniższym przykładzie pokazano, jak za pomocą metody Register anulować asynchroniczne żądanie sieci Web.The following example shows how to use the Register method to cancel an asynchronous Web request.

using System;
using System.Net;
using System.Threading;

class Example
{
    static void Main()
    {
        CancellationTokenSource cts = new CancellationTokenSource();

        StartWebRequest(cts.Token);

        // cancellation will cause the web 
        // request to be cancelled
        cts.Cancel();
    }

    static void StartWebRequest(CancellationToken token)
    {
        WebClient wc = new WebClient();
        wc.DownloadStringCompleted += (s, e) => Console.WriteLine("Request completed.");

        // Cancellation on the token will 
        // call CancelAsync on the WebClient.
        token.Register(() =>
        {
            wc.CancelAsync();
            Console.WriteLine("Request cancelled!");
        });

        Console.WriteLine("Starting request.");
        wc.DownloadStringAsync(new Uri("http://www.contoso.com"));
    }
}
Imports System.Net
Imports System.Threading

Class Example
    Private Shared Sub Main()
        Dim cts As New CancellationTokenSource()

        StartWebRequest(cts.Token)

        ' cancellation will cause the web 
        ' request to be cancelled
        cts.Cancel()
    End Sub

    Private Shared Sub StartWebRequest(token As CancellationToken)
        Dim wc As New WebClient()
        wc.DownloadStringCompleted += Function(s, e) Console.WriteLine("Request completed.")

        ' Cancellation on the token will 
        ' call CancelAsync on the WebClient.
        token.Register(Function() 
        wc.CancelAsync()
        Console.WriteLine("Request cancelled!")

End Function)

        Console.WriteLine("Starting request.")
        wc.DownloadStringAsync(New Uri("http://www.contoso.com"))
    End Sub
End Class

Obiekt CancellationTokenRegistration zarządza synchronizacją wątków i zapewnia, że wywołanie zwrotne zatrzyma się w określonym momencie.The CancellationTokenRegistration object manages thread synchronization and ensures that the callback will stop executing at a precise point in time.

Aby zapewnić czas odpowiedzi systemu i uniknąć zakleszczeń, należy przestrzegać następujących wytycznych podczas rejestrowania wywołań zwrotnych:In order to ensure system responsiveness and to avoid deadlocks, the following guidelines must be followed when registering callbacks:

  • Metoda wywołania zwrotnego powinna być szybka, ponieważ jest wywoływana synchronicznie i w związku z tym wywołanie Cancel nie zwraca do momentu powrotu wywołania zwrotnego.The callback method should be fast because it is called synchronously and therefore the call to Cancel does not return until the callback returns.

  • Jeśli wywołasz Dispose, gdy wywołanie zwrotne jest uruchomione i masz blokadę, która oczekuje na wywołanie zwrotne, program może być zakleszczony.If you call Dispose while the callback is running, and you hold a lock that the callback is waiting on, your program can deadlock. Po powrocie Dispose można zwolnić wszystkie zasoby wymagane przez wywołanie zwrotne.After Dispose returns, you can free any resources required by the callback.

  • Wywołania zwrotne nie powinny wykonywać żadnego wątku ręcznego ani SynchronizationContext użycia w wywołaniu zwrotnym.Callbacks should not perform any manual thread or SynchronizationContext usage in a callback. Jeśli wywołanie zwrotne musi zostać uruchomione w określonym wątku, użyj konstruktora System.Threading.CancellationTokenRegistration, który pozwala określić, że element docelowy syncContext jest aktywnym SynchronizationContext.Current.If a callback must run on a particular thread, use the System.Threading.CancellationTokenRegistration constructor that enables you to specify that the target syncContext is the active SynchronizationContext.Current. Ręczne wykonywanie wątków w wywołaniu zwrotnym może spowodować zakleszczenie.Performing manual threading in a callback can cause deadlock.

Aby zapoznać się z bardziej kompletnym przykładem, zobacz jak: rejestrowanie wywołań zwrotnych żądań anulowania.For a more complete example, see How to: Register Callbacks for Cancellation Requests.

Nasłuchiwanie przy użyciu dojścia oczekiwaniaListening by Using a Wait Handle

Gdy operacja anulowania może blokować się, gdy oczekuje na element pierwotny synchronizacji, taki jak System.Threading.ManualResetEvent lub System.Threading.Semaphore, można użyć właściwości CancellationToken.WaitHandle, aby umożliwić operacji oczekiwania na zdarzenie i żądanie anulowania.When a cancelable operation can block while it waits on a synchronization primitive such as a System.Threading.ManualResetEvent or System.Threading.Semaphore, you can use the CancellationToken.WaitHandle property to enable the operation to wait on both the event and the cancellation request. Dojście oczekiwania tokenu anulowania zostanie sygnalizowane w odpowiedzi na żądanie anulowania, a metoda może użyć wartości zwracanej metody WaitAny, aby określić, czy był to token anulowania, który zasygnalizuje.The wait handle of the cancellation token will become signaled in response to a cancellation request, and the method can use the return value of the WaitAny method to determine whether it was the cancellation token that signaled. Operacja może po prostu zakończyć lub zgłosić OperationCanceledException, zgodnie z potrzebami.The operation can then just exit, or throw a OperationCanceledException, as appropriate.

// Wait on the event if it is not signaled.
int eventThatSignaledIndex =
       WaitHandle.WaitAny(new WaitHandle[] { mre, token.WaitHandle },
                          new TimeSpan(0, 0, 20));
' 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))

W nowym kodzie, który jest przeznaczony dla .NET Framework 4, System.Threading.ManualResetEventSlim i System.Threading.SemaphoreSlim obie obsługują nową strukturę anulowania w swoich metodach Wait.In new code that targets the .NET Framework 4, System.Threading.ManualResetEventSlim and System.Threading.SemaphoreSlim both support the new cancellation framework in their Wait methods. CancellationToken można przekazać do metody, a po zażądaniu anulowania zdarzenie zostanie wznowione i wygeneruje OperationCanceledException.You can pass the CancellationToken to the method, and when the cancellation is requested, the event wakes up and throws an OperationCanceledException.

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);
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)

Aby zapoznać się z bardziej kompletnym przykładem, zobacz jak: nasłuchiwanie żądań anulowania z dojściami oczekiwania.For a more complete example, see How to: Listen for Cancellation Requests That Have Wait Handles.

Jednoczesne nasłuchiwanie wielu tokenówListening to Multiple Tokens Simultaneously

W niektórych przypadkach odbiornik może chcieć nasłuchiwać wielu tokenów anulowania jednocześnie.In some cases, a listener may have to listen to multiple cancellation tokens simultaneously. Na przykład operacja anulowania może być konieczne do monitorowania wewnętrznego tokenu anulowania oprócz tokenu przesyłanego zewnętrznie jako argumentu do parametru metody.For example, a cancelable operation may have to monitor an internal cancellation token in addition to a token passed in externally as an argument to a method parameter. Aby to osiągnąć, Utwórz źródło połączonego tokenu, które może dołączyć dwa lub więcej tokenów do jednego tokenu, jak pokazano w poniższym przykładzie.To accomplish this, create a linked token source that can join two or more tokens into one token, as shown in the following example.

public void DoWork(CancellationToken externalToken)
{
   // Create a new token that combines the internal and external tokens.
   this.internalToken = internalTokenSource.Token;
   this.externalToken = externalToken;

   using (CancellationTokenSource linkedCts =
           CancellationTokenSource.CreateLinkedTokenSource(internalToken, externalToken))
   {
       try {
           DoWorkInternal(linkedCts.Token);
       }
       catch (OperationCanceledException) {
           if (internalToken.IsCancellationRequested) {
               Console.WriteLine("Operation timed out.");
           }
           else if (externalToken.IsCancellationRequested) {
               Console.WriteLine("Cancelling per user request.");
               externalToken.ThrowIfCancellationRequested();
           }
       }
   }
}
Public Sub DoWork(ByVal externalToken As CancellationToken)
   ' Create a new token that combines the internal and external tokens.
   Dim internalToken As CancellationToken = internalTokenSource.Token
   Dim linkedCts As CancellationTokenSource =
   CancellationTokenSource.CreateLinkedTokenSource(internalToken, externalToken)
   Using (linkedCts)
      Try
         DoWorkInternal(linkedCts.Token)
      Catch e As OperationCanceledException
         If e.CancellationToken = internalToken Then
            Console.WriteLine("Operation timed out.")
         ElseIf e.CancellationToken = externalToken Then
            Console.WriteLine("Canceled by external token.")
            externalToken.ThrowIfCancellationRequested()
         End If
      End Try
   End Using
End Sub

Zwróć uwagę, że po zakończeniu pracy z powiązanym źródłem tokenów należy wywołać Dispose.Notice that you must call Dispose on the linked token source when you are done with it. Aby zapoznać się z bardziej kompletnym przykładem, zobacz jak: nasłuchiwanie wielu żądań anulowania.For a more complete example, see How to: Listen for Multiple Cancellation Requests.

Współpraca między kodem biblioteki i kodem użytkownikaCooperation Between Library Code and User Code

Ujednolicona platforma anulowania pozwala na kod biblioteki, aby anulować kod użytkownika, a kod użytkownika w celu anulowania kodu biblioteki w sposób współpracy.The unified cancellation framework makes it possible for library code to cancel user code, and for user code to cancel library code in a cooperative manner. Bezproblemowa współpraca zależy od następujących wskazówek:Smooth cooperation depends on each side following these guidelines:

  • Jeśli kod biblioteki zapewnia operacje, które można anulować, powinien również dostarczyć metody publiczne, które akceptują Zewnętrzny token anulowania, aby kod użytkownika mógł żądać anulowania.If library code provides cancelable operations, it should also provide public methods that accept an external cancellation token so that user code can request cancellation.

  • Jeśli kod biblioteki wywołuje kod użytkownika, kod biblioteki powinien interpretować OperationCanceledException (externalToken) jako spółdzielnie anulowaniai niekoniecznie jako wyjątek błędu.If library code calls into user code, the library code should interpret an OperationCanceledException(externalToken) as cooperative cancellation, and not necessarily as a failure exception.

  • Delegaty użytkownika powinni próbować odpowiedzieć na żądania anulowania z kodu biblioteki w odpowiednim czasie.User-delegates should attempt to respond to cancellation requests from library code in a timely manner.

System.Threading.Tasks.Task i System.Linq.ParallelEnumerable są przykładami klas, które są zgodne z tymi wskazówkami.System.Threading.Tasks.Task and System.Linq.ParallelEnumerable are examples of classes that follow these guidelines. Aby uzyskać więcej informacji, zobacz Anulowanie zadania i instrukcje: anulowanie zapytania PLINQ.For more information, see Task Cancellation and How to: Cancel a PLINQ Query.

Zobacz takżeSee also