Cancelamento em threads gerenciadosCancellation in Managed Threads

A partir do .NET Framework 4, o .NET Framework usa um modelo unificado para cancelamento cooperativo de operações assíncronas ou síncronas de longa execução.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 é baseado em um objeto leve chamado token de cancelamento.This model is based on a lightweight object called a cancellation token. O objeto que invoca uma ou mais operações canceláveis, por exemplo criando novos tópicos ou tarefas, passa o token para cada operação.The object that invokes one or more cancelable operations, for example by creating new threads or tasks, passes the token to each operation. As operações individuais podem, por sua vez, passar cópias do token para outras operações.Individual operations can in turn pass copies of the token to other operations. Posteriormente, o objeto que criou o token pode usá-lo para solicitar que as operações parem o que estão fazendo.At some later time, the object that created the token can use it to request that the operations stop what they are doing. Somente o objeto solicitante pode emitir a solicitação de cancelamento, e cada ouvinte é responsável por perceber a solicitação e respondê-la de forma apropriada e oportuna.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.

O padrão geral para implementar o modelo de cancelamento cooperativo é:The general pattern for implementing the cooperative cancellation model is:

  • Instancie um objeto CancellationTokenSource, que gerencia e envia uma notificação de cancelamento para os tokens de cancelamento individuais.Instantiate a CancellationTokenSource object, which manages and sends cancellation notification to the individual cancellation tokens.

  • Passe o token retornado pela propriedade CancellationTokenSource.Token para cada tarefa ou thread que responde ao cancelamento.Pass the token returned by the CancellationTokenSource.Token property to each task or thread that listens for cancellation.

  • Forneça um mecanismo para cada tarefa ou thread responder ao cancelamento.Provide a mechanism for each task or thread to respond to cancellation.

  • Chame o método CancellationTokenSource.Cancel para fornecer uma notificação de cancelamento.Call the CancellationTokenSource.Cancel method to provide notification of cancellation.

Importante

A classe CancellationTokenSource implementa a interface IDisposable.The CancellationTokenSource class implements the IDisposable interface. Certifique-se de chamar o método CancellationTokenSource.Dispose quando terminar de usar a fonte de token de cancelamento para liberar todos os recursos não gerenciados detidos.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.

A ilustração a seguir mostra o relacionamento entre uma fonte de token e todas as cópias de seu token.The following illustration shows the relationship between a token source and all the copies of its token.

CancellationTokenSource e tokens de cancelamentoCancellationTokenSource and cancellation tokens

O novo modelo de cancelamento facilita a criação de aplicativos e bibliotecas com reconhecimento de cancelamento, e ele dá suporte aos seguintes recursos:The new cancellation model makes it easier to create cancellation-aware applications and libraries, and it supports the following features:

  • O cancelamento é cooperativo e não será forçado ao ouvinte.Cancellation is cooperative and is not forced on the listener. O ouvinte determina como encerrar normalmente em resposta a um pedido de cancelamento.The listener determines how to gracefully terminate in response to a cancellation request.

  • Solicitar é diferente de detectar.Requesting is distinct from listening. Um objeto que invoca uma operação cancelável pode controlar quando (e se) o cancelamento será solicitado.An object that invokes a cancelable operation can control when (if ever) cancellation is requested.

  • O objeto solicitante emite o pedido de cancelamento para todas as cópias do token usando apenas uma chamada de método.The requesting object issues the cancellation request to all copies of the token by using just one method call.

  • Um ouvinte pode detectar vários tokens simultaneamente juntando-os a um token vinculado.A listener can listen to multiple tokens simultaneously by joining them into one linked token.

  • O código do usuário pode notar e responder às solicitações de cancelamento do código da biblioteca e o código da biblioteca pode observar e responder às solicitações de cancelamento do código do usuário.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.

  • Os ouvintes podem ser notificados sobre solicitações de cancelamento por sondagem, registro de retorno de chamada ou aguardando identificadores de espera.Listeners can be notified of cancellation requests by polling, callback registration, or waiting on wait handles.

Tipos de cancelamentoCancellation Types

A estrutura de cancelamento é implementada como um conjunto de tipos relacionados, que estão listados na tabela a seguir.The cancellation framework is implemented as a set of related types, which are listed in the following table.

Nome de tipoType name DESCRIÇÃODescription
CancellationTokenSource O objeto que cria um token de cancelamento, e também emite o pedido de cancelamento para todas as cópias desse token.Object that creates a cancellation token, and also issues the cancellation request for all copies of that token.
CancellationToken O tipo de valor leve passado a um ou mais ouvintes, normalmente como um parâmetro de método.Lightweight value type passed to one or more listeners, typically as a method parameter. Os ouvintes monitoram o valor da propriedade IsCancellationRequested do token por sondagem, retorno de chamada ou identificador de espera.Listeners monitor the value of the IsCancellationRequested property of the token by polling, callback, or wait handle.
OperationCanceledException As sobrecargas do construtor desta exceção aceitam CancellationToken como um parâmetro.Overloads of this exception's constructor accept a CancellationToken as a parameter. Os ouvintes podem, opcionalmente, lançar essa exceção para verificar a origem do cancelamento e notificar aos outros que ela respondeu a uma solicitação de cancelamento.Listeners can optionally throw this exception to verify the source of the cancellation and notify others that it has responded to a cancellation request.

O novo modelo de cancelamento está integrado ao .NET Framework em vários tipos.The new cancellation model is integrated into the .NET Framework in several types. Os mais importantes são System.Threading.Tasks.Parallel, System.Threading.Tasks.Task, System.Threading.Tasks.Task<TResult> e 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. Recomendamos o uso desse novo modelo de cancelamento para todas as novas bibliotecas e códigos de aplicativos.We recommend that you use this new cancellation model for all new library and application code.

Exemplo de códigoCode Example

No exemplo a seguir, o objeto solicitante cria um objeto CancellationTokenSource e, em seguida, passa sua propriedade Token para a operação cancelável.In the following example, the requesting object creates a CancellationTokenSource object, and then passes its Token property to the cancelable operation. A operação que recebe a solicitação monitora o valor da propriedade IsCancellationRequested do token por sondagem.The operation that receives the request monitors the value of the IsCancellationRequested property of the token by polling. Quando o valor se torna true, o ouvinte pode concluir de qualquer maneira apropriada.When the value becomes true, the listener can terminate in whatever manner is appropriate. Neste exemplo, o método apenas sai, que é tudo necessário em muitos casos.In this example, the method just exits, which is all that is required in many cases.

Observação

O exemplo usa o método QueueUserWorkItem para demonstrar que a nova estrutura de cancelamento é compatível com as APIs legadas.The example uses the QueueUserWorkItem method to demonstrate that the new cancellation framework is compatible with legacy APIs. Para obter um exemplo que usa o novo tipo System.Threading.Tasks.Task preferencial, veja Como: Cancelar uma tarefa e seus filhos.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...

Cancelamento de operação versus cancelamento de objetoOperation Cancellation Versus Object Cancellation

Na nova estrutura de cancelamento, o cancelamento refere-se a operações, não a objetos.In the new cancellation framework, cancellation refers to operations, not objects. A solicitação de cancelamento significa que a operação deve ser interrompida o mais rápido possível após a conclusão de qualquer limpeza necessária.The cancellation request means that the operation should stop as soon as possible after any required cleanup is performed. Um token de cancelamento deve se referir a uma "operação cancelável", no entanto, essa operação pode ser implementada em seu programa.One cancellation token should refer to one "cancelable operation," however that operation may be implemented in your program. Depois que a propriedade IsCancellationRequested do token foi configurada para true, não pode ser redefinida para false.After the IsCancellationRequested property of the token has been set to true, it cannot be reset to false. Portanto, os tokens de cancelamento não podem ser reutilizados depois de terem sido cancelados.Therefore, cancellation tokens cannot be reused after they have been canceled.

Se você precisar de um mecanismo de cancelamento de objeto, pode baseá-lo no mecanismo de cancelamento da operação chamando o método CancellationToken.Register, como mostrado no exemplo a seguir.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

Se um objeto oferecer suporte a mais de uma operação simultânea cancelável, passe um token separado como entrada para cada operação cancelável distinta.If an object supports more than one concurrent cancelable operation, pass a separate token as input to each distinct cancelable operation. Dessa forma, uma operação pode ser cancelada sem afetar as outras.That way, one operation can be cancelled without affecting the others.

Detectar e responder a solicitações de cancelamentoListening and Responding to Cancellation Requests

No delegado do usuário, o implementador de uma operação cancelável determina como concluir a operação em resposta a uma solicitação de cancelamento.In the user delegate, the implementer of a cancelable operation determines how to terminate the operation in response to a cancellation request. Em muitos casos, o delegado do usuário pode apenas executar qualquer limpeza necessária e depois retornar imediatamente.In many cases, the user delegate can just perform any required cleanup and then return immediately.

No entanto, em casos mais complexos, pode ser necessário que o delegado do usuário notifique ao código da biblioteca que ocorreu o cancelamento.However, in more complex cases, it might be necessary for the user delegate to notify library code that cancellation has occurred. Nesses casos, a maneira correta de concluir a operação é o delegado chamar o método ThrowIfCancellationRequested, que fará com que um OperationCanceledException seja lançado.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. O código da biblioteca pode capturar essa exceção no thread do delegado de usuário e examinar o token da exceção para determinar se a exceção indica cancelamento cooperativo ou alguma outra situação 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.

A classe Task lida com o OperationCanceledException dessa forma.The Task class handles OperationCanceledException in this way. Para obter mais informações, consulte Cancelamento de tarefas.For more information, see Task Cancellation.

Detectar por sondagemListening by Polling

Para cálculos de longa execução que fazem loop ou repetição, você pode detectar uma solicitação de cancelamento periodicamente pesquisando o valor da propriedade 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. Se o valor for true, o método deve ser limpo e concluído o mais rápido possível.If its value is true, the method should clean up and terminate as quickly as possible. A frequência de pesquisa ideal depende do tipo de aplicativo.The optimal frequency of polling depends on the type of application. Cabe ao desenvolvedor determinar a melhor frequência de sondagem para qualquer programa.It is up to the developer to determine the best polling frequency for any given program. A sondagem em si não afeta o desempenho significativamente.Polling itself does not significantly impact performance. O exemplo a seguir mostra uma abordagem possível para a sondagem.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 obter um exemplo mais completo, confira Como: Escutar solicitações de cancelamento por sondagem.For a more complete example, see How to: Listen for Cancellation Requests by Polling.

Ouvir ao registrar um retorno de chamadaListening by Registering a Callback

Algumas operações podem ser bloqueadas de tal forma que não podem verificar o valor do token de cancelamento em tempo hábil.Some operations can become blocked in such a way that they cannot check the value of the cancellation token in a timely manner. Para esses casos, você pode registrar um método de retorno de chamada que desbloqueia o método quando um pedido de cancelamento é recebido.For these cases, you can register a callback method that unblocks the method when a cancellation request is received.

O método Register retorna um objeto CancellationTokenRegistration que é usado especificamente para essa finalidade.The Register method returns a CancellationTokenRegistration object that is used specifically for this purpose. O exemplo a seguir mostra como usar o método Register para cancelar uma solicitação da Web assíncrona.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

O objeto CancellationTokenRegistration gerencia a sincronização de thread e garante que o retorno de chamada será interrompido em um determinado momento.The CancellationTokenRegistration object manages thread synchronization and ensures that the callback will stop executing at a precise point in time.

Para garantir a capacidade de resposta do sistema e evitar deadlocks, as seguintes diretrizes devem ser seguidas ao registrar retornos de chamada:In order to ensure system responsiveness and to avoid deadlocks, the following guidelines must be followed when registering callbacks:

  • O método de retorno de chamada deve ser rápido porque é chamado de forma síncrona e, portanto, a chamada para Cancel não retorna até o retorno da chamada.The callback method should be fast because it is called synchronously and therefore the call to Cancel does not return until the callback returns.

  • Se você chamar o Dispose enquanto o retorno de chamada estiver sendo executado e segurar um bloqueio em que o retorno de chamada está aguardando, seu programa pode chegar a um deadlock.If you call Dispose while the callback is running, and you hold a lock that the callback is waiting on, your program can deadlock. Depois do retorno de Dispose, você pode liberar todos os recursos necessários para o retorno de chamada.After Dispose returns, you can free any resources required by the callback.

  • Os retornos de chamada não devem executar qualquer thread manual ou uso de SynchronizationContext em um retorno de chamada.Callbacks should not perform any manual thread or SynchronizationContext usage in a callback. Se um retorno de chamada deve ser executado em um thread específico, use o construtor System.Threading.CancellationTokenRegistration que permite especificar que o destino syncContext é o SynchronizationContext.Current ativo.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. Realizar threading manual em um retorno de chamada pode causar um deadlock.Performing manual threading in a callback can cause deadlock.

Para obter um exemplo mais completo, confira Como: Registrar retornos de chamada para solicitações de cancelamento.For a more complete example, see How to: Register Callbacks for Cancellation Requests.

Ouvir usando um identificador de esperaListening by Using a Wait Handle

Quando uma operação cancelável pode bloquear enquanto aguarda um primitivo de sincronização, como System.Threading.ManualResetEvent ou System.Threading.Semaphore, você pode usar a propriedade CancellationToken.WaitHandle para permitir que a operação aguarde o evento e a solicitação de cancelamento.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. O identificador de espera do token de cancelamento será sinalizado em resposta a um pedido de cancelamento e o método pode usar o valor de retorno do método WaitAny para determinar se foi o token de cancelamento que sinalizou.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 operação poderá, em seguida, simplesmente sair ou lançar um OperationCanceledException, conforme apropriado.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))

No novo código que tem como alvo o .NET Framework 4, System.Threading.ManualResetEventSlim e System.Threading.SemaphoreSlim dão suporte à nova estrutura de cancelamento em seus 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. Você pode passar o CancellationToken para o método, e quando o cancelamento é solicitado, o evento é acionado e lança um 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 obter um exemplo mais completo, confira Como: Escutar solicitações de cancelamento que têm identificadores de espera.For a more complete example, see How to: Listen for Cancellation Requests That Have Wait Handles.

Detectar vários tokens simultaneamenteListening to Multiple Tokens Simultaneously

Em alguns casos, um ouvinte pode ter que detectar vários tokens de cancelamento simultaneamente.In some cases, a listener may have to listen to multiple cancellation tokens simultaneously. Por exemplo, uma operação cancelável pode ter que monitorar um token de cancelamento interno, além de um token passado externamente como um argumento para um 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 fazer isso, crie uma fonte de token vinculada que pode juntar dois ou mais tokens em um token, como mostrado no exemplo a seguir.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

Observe que você deve chamar Dispose na fonte de token vinculada quando concluir.Notice that you must call Dispose on the linked token source when you are done with it. Para obter um exemplo mais completo, confira Como: Escutar várias solicitações de cancelamento.For a more complete example, see How to: Listen for Multiple Cancellation Requests.

Cooperação entre o código de biblioteca e o código do usuárioCooperation Between Library Code and User Code

A estrutura de cancelamento unificada permite que o código da biblioteca cancele o código do usuário e que o código do usuário cancele o código da biblioteca de forma 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. A cooperação sem problemas depende da conformidade com as seguintes diretrizes:Smooth cooperation depends on each side following these guidelines:

  • Se o código da biblioteca fornece operações canceláveis, ele também deve fornecer métodos públicos que aceitam um token de cancelamento externo para que o código do usuário possa solicitar o cancelamento.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.

  • Se o código da biblioteca chamar o código do usuário, o código da biblioteca deve interpretar um OperationCanceledException(externalToken) como cancelamento cooperativo e não necessariamente como uma exceção de falha.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.

  • Os delegados de usuários devem tentar responder as solicitações de cancelamento do código da biblioteca em tempo hábil.User-delegates should attempt to respond to cancellation requests from library code in a timely manner.

System.Threading.Tasks.Task e System.Linq.ParallelEnumerable são exemplos de classes que seguem essas diretrizes.System.Threading.Tasks.Task and System.Linq.ParallelEnumerable are examples of classes that follow these guidelines. Para obter mais informações, confira Cancelamento de tarefas e Como: Cancelar uma consulta PLINQ.For more information, see Task Cancellation and How to: Cancel a PLINQ Query.

Consulte tambémSee also