Cancelación en subprocesos administradosCancellation in Managed Threads

A partir de .NET Framework 4, .NET Framework usa un modelo unificado para la cancelación cooperativa de operaciones asincrónicas o sincrónicas de ejecución prolongada.Starting with the .NET Framework 4, the .NET Framework uses a unified model for cooperative cancellation of asynchronous or long-running synchronous operations. Este modelo se basa en un objeto ligero denominado token de cancelación.This model is based on a lightweight object called a cancellation token. El objeto que invoca una o más operaciones cancelables, por ejemplo creando un nuevo subproceso o tarea, pasa el token a cada operación.The object that invokes one or more cancelable operations, for example by creating new threads or tasks, passes the token to each operation. Las operaciones individuales pueden pasar a su vez copias del token a otras operaciones.Individual operations can in turn pass copies of the token to other operations. En algún momento posterior, el objeto que creó el token puede usarlo para solicitar que las operaciones se detengan.At some later time, the object that created the token can use it to request that the operations stop what they are doing. Solo el objeto solicitante puede emitir la solicitud de cancelación y cada agente de escucha es responsable de observar la solicitud y responder a ella de manera puntual.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.

El patrón general para implementar el modelo de cancelación cooperativa es:The general pattern for implementing the cooperative cancellation model is:

  • Crear una instancia de un objeto CancellationTokenSource, que administra y envía una notificación de cancelación a los tokens de cancelación individuales.Instantiate a CancellationTokenSource object, which manages and sends cancellation notification to the individual cancellation tokens.

  • Pasar el token devuelto por la propiedad CancellationTokenSource.Token para cada tarea o el subproceso que realiza escuchas de cancelación.Pass the token returned by the CancellationTokenSource.Token property to each task or thread that listens for cancellation.

  • Proporcionar un mecanismo para que cada tarea o subproceso responda a la cancelación.Provide a mechanism for each task or thread to respond to cancellation.

  • Llamar al método CancellationTokenSource.Cancel para proporcionar una notificación de cancelación.Call the CancellationTokenSource.Cancel method to provide notification of cancellation.

Importante

La clase CancellationTokenSource implementa la interfaz IDisposable.The CancellationTokenSource class implements the IDisposable interface. Debe asegurarse de llamar al método CancellationTokenSource.Dispose cuando termine de usar el origen del token de cancelación para liberar los recursos no administrados que contiene.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.

En la siguiente ilustración se muestra la relación entre un origen de token y todas las copias de su token.The following illustration shows the relationship between a token source and all the copies of its token.

CancellationTokenSource y tokens de cancelaciónCancellationTokenSource and cancellation tokens

El nuevo modelo de cancelación facilita la creación de bibliotecas y aplicaciones compatibles con la cancelación y admite las siguientes características:The new cancellation model makes it easier to create cancellation-aware applications and libraries, and it supports the following features:

  • La cancelación es cooperativa y no se impone al agente de escucha.Cancellation is cooperative and is not forced on the listener. El agente de escucha determina cómo finalizar correctamente en respuesta a una solicitud de cancelación.The listener determines how to gracefully terminate in response to a cancellation request.

  • La solicitud es distinta que la escucha.Requesting is distinct from listening. Un objeto que invoca una operación cancelable puede controlar cuándo se solicita la cancelación (si se solicita).An object that invokes a cancelable operation can control when (if ever) cancellation is requested.

  • El objeto solicitante emite la solicitud de cancelación para todas las copias del token usando simplemente una llamada de método.The requesting object issues the cancellation request to all copies of the token by using just one method call.

  • Si los une en un token vinculado, un agente de escucha puede escuchar varios tokens simultáneamente.A listener can listen to multiple tokens simultaneously by joining them into one linked token.

  • El código de usuario puede observar y responder a las solicitudes de cancelación desde código de biblioteca y el código de biblioteca puede observar y responder a las solicitudes de cancelación desde código de usuario.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.

  • Los agentes de escucha pueden recibir las solicitudes de cancelación mediante sondeo, registro de devolución de llamada o espera en identificadores de espera.Listeners can be notified of cancellation requests by polling, callback registration, or waiting on wait handles.

Tipos de cancelaciónCancellation Types

El marco de cancelación se implementa como un conjunto de tipos relacionados. Estos tipos se enumeran en la tabla siguiente.The cancellation framework is implemented as a set of related types, which are listed in the following table.

Nombre de tipoType name DESCRIPCIÓNDescription
CancellationTokenSource Objeto que se crea un token de cancelación y también emite la solicitud de cancelación para todas las copias de ese token.Object that creates a cancellation token, and also issues the cancellation request for all copies of that token.
CancellationToken Tipo de valor ligero pasado a uno o varios agentes de escucha, normalmente como un parámetro de método.Lightweight value type passed to one or more listeners, typically as a method parameter. Los agentes de escucha supervisan el valor de la propiedad IsCancellationRequested del token mediante sondeo, devolución de llamada o identificador de espera.Listeners monitor the value of the IsCancellationRequested property of the token by polling, callback, or wait handle.
OperationCanceledException Las sobrecargas del constructor de esta excepción aceptan CancellationToken como parámetro.Overloads of this exception's constructor accept a CancellationToken as a parameter. Los agentes de escucha pueden generar esta excepción para comprobar el origen de la cancelación y notificar a otros que ha respondido a una solicitud de cancelación.Listeners can optionally throw this exception to verify the source of the cancellation and notify others that it has responded to a cancellation request.

El nuevo modelo de cancelación se integra en .NET Framework en varios tipos.The new cancellation model is integrated into the .NET Framework in several types. Los más importantes son System.Threading.Tasks.Parallel, System.Threading.Tasks.Task, System.Threading.Tasks.Task<TResult> y 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. Le recomendamos usar este nuevo modelo de cancelación para todo el código de biblioteca y aplicación nuevo.We recommend that you use this new cancellation model for all new library and application code.

Ejemplo de códigoCode Example

En el ejemplo siguiente, el objeto solicitante crea un objeto CancellationTokenSource y, a continuación, pasa su propiedad Token a la operación cancelable.In the following example, the requesting object creates a CancellationTokenSource object, and then passes its Token property to the cancelable operation. La operación que recibe la solicitud supervisa el valor de la propiedad IsCancellationRequested del token mediante sondeo.The operation that receives the request monitors the value of the IsCancellationRequested property of the token by polling. Cuando el valor se convierte en true, el agente de escucha puede finalizar de la manera adecuada.When the value becomes true, the listener can terminate in whatever manner is appropriate. En este ejemplo el método simplemente sale, que es lo único necesario en muchos casos.In this example, the method just exits, which is all that is required in many cases.

Nota

En el ejemplo se usa el método QueueUserWorkItem para demostrar que el nuevo marco de cancelación es compatible con las API heredadas.The example uses the QueueUserWorkItem method to demonstrate that the new cancellation framework is compatible with legacy APIs. Para obtener un ejemplo en donde se utiliza el nuevo tipo preferido, System.Threading.Tasks.Task, vea Procedimiento: Cancelar una tarea y sus elementos secundarios.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...

Cancelación de operaciones frente a cancelación de objetosOperation Cancellation Versus Object Cancellation

En el nuevo marco de cancelación, la cancelación se refiere a las operaciones, no a objetos.In the new cancellation framework, cancellation refers to operations, not objects. La solicitud de cancelación significa que la operación debe detenerse lo antes posible después de realizar cualquier limpieza necesaria.The cancellation request means that the operation should stop as soon as possible after any required cleanup is performed. Un token de cancelación debe hacer referencia a una "operación cancelable", independientemente de si esa operación está implementada en su programa.One cancellation token should refer to one "cancelable operation," however that operation may be implemented in your program. Después de establecer la propiedad IsCancellationRequested del token en true, no puede restablecerse a false.After the IsCancellationRequested property of the token has been set to true, it cannot be reset to false. Por lo tanto, los tokens de cancelación no pueden volver a usarse una vez cancelados.Therefore, cancellation tokens cannot be reused after they have been canceled.

Si necesita un mecanismo de cancelación de objetos, puede basarlo en el mecanismo de cancelación de operaciones mediante una llamada al método CancellationToken.Register, tal como se muestra en el ejemplo siguiente.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

Si un objeto admite más de una operación cancelable simultánea, pase un token independiente como entrada para cada operación cancelable.If an object supports more than one concurrent cancelable operation, pass a separate token as input to each distinct cancelable operation. De este modo, se puede cancelar una operación sin que afecte al resto.That way, one operation can be cancelled without affecting the others.

Escucha y respuesta a solicitudes de cancelaciónListening and Responding to Cancellation Requests

En el delegado de usuario, el implementador de una operación cancelable determina cómo finalizar la operación en respuesta a una solicitud de cancelación.In the user delegate, the implementer of a cancelable operation determines how to terminate the operation in response to a cancellation request. En muchos casos, el delegado de usuario puede realizar simplemente cualquier limpieza necesaria y volver inmediatamente.In many cases, the user delegate can just perform any required cleanup and then return immediately.

Sin embargo, en casos más complejos, es posible que sea necesario que el delegado de usuario notifique al código de biblioteca que se ha producido la cancelación.However, in more complex cases, it might be necessary for the user delegate to notify library code that cancellation has occurred. En estos casos, la manera correcta de finalizar la operación es que el delegado llame al método ThrowIfCancellationRequested, lo que provocará que se genere 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. El código de biblioteca puede detectar esta excepción en el subproceso de delegado de usuario y examinar el token de la excepción para determinar si la excepción indica una cancelación cooperativa o alguna otra situación excepcional.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.

La clase Task administra OperationCanceledException de esta manera.The Task class handles OperationCanceledException in this way. Para más información, vea Task Cancellation.For more information, see Task Cancellation.

Escuchas mediante sondeoListening by Polling

Para los cálculos de ejecución prolongada que se repiten, puede escuchar una solicitud de cancelación sondeando periódicamente el valor de la propiedad 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. Si su valor es true, el método debe realizar una limpieza y finalizar lo antes posible.If its value is true, the method should clean up and terminate as quickly as possible. La frecuencia óptima de sondeo depende del tipo de aplicación.The optimal frequency of polling depends on the type of application. Es el desarrollador quien determina la mejor frecuencia de sondeo para cualquier programa dado.It is up to the developer to determine the best polling frequency for any given program. El sondeo en sí no afecta significativamente al rendimiento.Polling itself does not significantly impact performance. En el ejemplo siguiente se muestra una posible manera de sondeo.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

Para obtener un ejemplo más completo, vea Cómo: Escucha de solicitudes de cancelación mediante sondeo.For a more complete example, see How to: Listen for Cancellation Requests by Polling.

Escuchas mediante el registro de una devolución de llamadaListening by Registering a Callback

Algunas operaciones se pueden bloquear de forma que no pueden comprobar el valor del token de cancelación de manera oportuna.Some operations can become blocked in such a way that they cannot check the value of the cancellation token in a timely manner. En estos casos, se puede registrar un método de devolución de llamada que desbloquee el método cuando se reciba una solicitud de cancelación.For these cases, you can register a callback method that unblocks the method when a cancellation request is received.

El método Register devuelve un objeto CancellationTokenRegistration que se usa específicamente para este propósito.The Register method returns a CancellationTokenRegistration object that is used specifically for this purpose. En el ejemplo siguiente se muestra cómo usar el método Register para cancelar una solicitud web asincrónica.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

El objeto CancellationTokenRegistration administra la sincronización de subprocesos y garantiza que la devolución de llamada dejará de ejecutarse en un momento concreto en el tiempo.The CancellationTokenRegistration object manages thread synchronization and ensures that the callback will stop executing at a precise point in time.

Para garantizar la capacidad de respuesta del sistema y evitar los interbloqueos, deben seguirse las siguientes directrices al registrar devoluciones de llamada:In order to ensure system responsiveness and to avoid deadlocks, the following guidelines must be followed when registering callbacks:

  • El método de devolución de llamada debe ser rápido porque se llama sincrónicamente y, por tanto, la llamada a Cancel no devuelve un valor hasta que no se devuelve la devolución de llamada.The callback method should be fast because it is called synchronously and therefore the call to Cancel does not return until the callback returns.

  • Si se llama a Dispose mientras se ejecuta la devolución de llamada y se mantiene un bloqueo que está esperando la devolución de llamada, el programa puede causar interbloqueos.If you call Dispose while the callback is running, and you hold a lock that the callback is waiting on, your program can deadlock. Después de que Dispose devuelve un valor, puede liberar todos los recursos requeridos por la devolución de llamada.After Dispose returns, you can free any resources required by the callback.

  • Las devoluciones de llamada no deben realizar ningún subproceso manual ni uso de o SynchronizationContext en una devolución de llamada.Callbacks should not perform any manual thread or SynchronizationContext usage in a callback. Si una devolución de llamada debe ejecutarse en un subproceso concreto, use el constructor System.Threading.CancellationTokenRegistration que le permite especificar que la clase syncContext de destino es el SynchronizationContext.Current activo.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. Si se realiza un subproceso manual en una devolución de llamada, puede producirse un interbloqueo.Performing manual threading in a callback can cause deadlock.

Para obtener un ejemplo más completo, vea Cómo: Registro de devoluciones de llamada como solicitudes de cancelación.For a more complete example, see How to: Register Callbacks for Cancellation Requests.

Escuchas mediante un identificador de esperaListening by Using a Wait Handle

Cuando una operación cancelable puede bloquearse mientras espera en una primitiva de sincronización como System.Threading.ManualResetEvent o System.Threading.Semaphore, se puede usar la propiedad CancellationToken.WaitHandle para habilitar la operación de espera en el evento y la solicitud de cancelación.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. El identificador de espera del token de cancelación se señalará en respuesta a una solicitud de cancelación y el método puede usar el valor devuelto del método WaitAny para determinar si era el token de cancelación el que señalaba.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. A continuación, la operación puede cerrarse o generar OperationCanceledException, según corresponda.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))

En el nuevo código destinado a .NET Framework 4, System.Threading.ManualResetEventSlim y System.Threading.SemaphoreSlim admiten el nuevo marco de cancelación en sus métodos 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 puede pasarse al método y, cuando se solicita la cancelación, el evento se activa y genera 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)

Para obtener un ejemplo más completo, vea Cómo: Escucha de solicitudes de cancelación que tienen identificadores de espera.For a more complete example, see How to: Listen for Cancellation Requests That Have Wait Handles.

Escucha de varios tokens simultáneamenteListening to Multiple Tokens Simultaneously

En algunos casos, un agente de escucha tiene que escuchar varios tokens de cancelación de manera simultánea.In some cases, a listener may have to listen to multiple cancellation tokens simultaneously. Por ejemplo, una operación cancelable puede tener que supervisar un token de cancelación interno además de un token pasado externamente como argumento a un parámetro de método.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. Para lograr esto, cree un origen de tokens vinculados que pueda combinar dos o más tokens en uno, como se muestra en el ejemplo siguiente.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

Tenga en cuenta que debe llamar a Dispose en el origen de tokens vinculados cuando haya terminado con él.Notice that you must call Dispose on the linked token source when you are done with it. Para obtener un ejemplo más completo, vea Cómo: Escucha de varias solicitudes de cancelación.For a more complete example, see How to: Listen for Multiple Cancellation Requests.

Cooperación entre código de biblioteca y código de usuarioCooperation Between Library Code and User Code

El marco de cancelación unificada hace posible que el código de biblioteca pueda cancelar el código de usuario y que el código de usuario pueda cancelar el código de la biblioteca de manera cooperativa.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. Una buena cooperación depende de que cada lado siga estas instrucciones:Smooth cooperation depends on each side following these guidelines:

  • Si el código de biblioteca proporciona operaciones cancelables, también debe proporcionar métodos públicos que acepten un token de cancelación externo para que el código de usuario pueda solicitar la cancelación.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.

  • Si el código de biblioteca llama al código de usuario, el código de biblioteca debe interpretar OperationCanceledException (externalToken) como cancelación cooperativa y no necesariamente como una excepción de error.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.

  • Los delegados de usuario deben intentar responder a las solicitudes de cancelación del código de biblioteca de manera adecuada.User-delegates should attempt to respond to cancellation requests from library code in a timely manner.

System.Threading.Tasks.Task y System.Linq.ParallelEnumerable son ejemplos de clases que siguen estas instrucciones.System.Threading.Tasks.Task and System.Linq.ParallelEnumerable are examples of classes that follow these guidelines. Para más información, vea Cancelación de tareas y Cómo: Cancelar una consulta PLINQ.For more information, see Task Cancellation and How to: Cancel a PLINQ Query.

Vea tambiénSee also