Token de cancelamento no construtor de tarefas: por quê?

Determinados construtores System.Threading.Tasks.Task tomam um CancellationToken como um parâmetro:

 CancellationTokenSource source = new CancellationTokenSource(); Task t = new Task (/* method */, source.Token); 

O que me deixa perplexo é que não há como, de dentro do corpo do método, chegar ao token passado (por exemplo, nada como Task.CurrentTask.CancellationToken ). O token deve ser fornecido por meio de outro mecanismo, como o object de estado ou capturado em um lambda.

Então, qual é o propósito de fornecer o token de cancelamento no construtor?

A passagem desse token para o construtor Task associa-o a essa tarefa.

Citando a resposta de Stephen Toub da MSDN :

Isso tem dois benefícios principais:

  1. Se o token tiver o cancelamento solicitado antes de a tarefa começar a ser executada, a tarefa não será executada. Em vez de fazer a transição para a Execução, a transição será imediatamente para Cancelada. Isso evita os custos de execução da tarefa se ela for cancelada durante a execução de qualquer maneira.
  2. Se o corpo da tarefa também estiver monitorando o token de cancelamento e lançar uma OperationCanceledException contendo esse token (que é o que ThrowIfCancellationRequested faz), quando a tarefa vir o OCE, verificará se o token do OCE corresponde ao token da tarefa. Em caso afirmativo, essa exceção é vista como um reconhecimento de cancelamento cooperativo e o estado de transições de tarefa para o estado cancelado (em vez do estado com falha).

O construtor usa o token para tratamento de cancelamento internamente. Se o seu código quiser acessar o token, você é responsável por passá-lo para si mesmo. Eu recomendaria altamente ler o livro Parallel Programming with Microsoft .NET no CodePlex .

Exemplo de uso do CTS do livro:

 CancellationTokenSource cts = new CancellationTokenSource(); CancellationToken token = cts.Token; Task myTask = Task.Factory.StartNew(() => { for (...) { token.ThrowIfCancellationRequested(); // Body of for loop. } }, token); // ... elsewhere ... cts.Cancel(); 

O cancelamento não é um caso simples, como muitos podem pensar. Algumas das sutilezas são explicadas neste post no msdn:

Por exemplo:

Em certas situações em Extensões Paralelas e em outros sistemas, é necessário acordar um método bloqueado por motivos que não são devidos ao cancelamento explícito de um usuário. Por exemplo, se um thread for bloqueado em blockingCollection.Take () devido à coleção estar vazia e outro thread subsequentemente chamar blockingCollection.CompleteAdding (), a primeira chamada deverá ser ativada e lançar um InvalidOperationException para representar um uso incorreto.

http://blogs.msdn.com/b/pfxteam/archive/2009/06/22/9791840.aspx