Eu estou procurando uma justificativa do motivo pelo qual a CancellationToken
estrutura do .NET foi introduzida além da CancellationTokenSource
classe. Entendo como a API deve ser usada, mas também quero entender por que ela foi projetada dessa maneira.
Ou seja, por que temos:
var cts = new CancellationTokenSource();
SomeCancellableOperation(cts.Token);
...
public void SomeCancellableOperation(CancellationToken token) {
...
token.ThrowIfCancellationRequested();
...
}
em vez de passar diretamente CancellationTokenSource
como:
var cts = new CancellationTokenSource();
SomeCancellableOperation(cts);
...
public void SomeCancellableOperation(CancellationTokenSource cts) {
...
cts.ThrowIfCancellationRequested();
...
}
Isso é uma otimização de desempenho com base no fato de que as verificações de estado de cancelamento ocorrem com mais frequência do que a transmissão do token?
Para que você CancellationTokenSource
possa acompanhar e atualizar CancellationTokens
, e para cada token a verificação de cancelamento é um acesso de campo local?
Dado que um bool volátil sem travamento é suficiente nos dois casos, ainda não consigo entender por que isso seria mais rápido.
Obrigado!
fonte
Eu tinha a pergunta exata e queria entender a lógica por trás desse design.
A resposta aceita acertou na lógica. Aqui está a confirmação da equipe que criou esse recurso (ênfase minha):
Link: Estrutura de cancelamento do .NET 4
Na minha opinião, o fato de
CancellationToken
apenas observar o estado e não alterá-lo é extremamente crítico. Você pode distribuir o token como um doce e nunca se preocupar que alguém, além de você, o cancele. Ele protege você contra código hostil de terceiros. Sim, as chances são pequenas, mas eu pessoalmente gosto dessa garantia.Eu também sinto que isso torna a API mais limpa, evita erros acidentais e promove um melhor design de componentes.
Vamos dar uma olhada na API pública para essas duas classes.
Se você combiná-los, ao escrever LongRunningFunction, verei métodos como as várias sobrecargas de 'Cancel' que não devo usar. Pessoalmente, eu odeio ver o método Dispose também.
Eu acho que o design da classe atual segue a filosofia do 'poço do sucesso', orienta os desenvolvedores a criar melhores componentes que podem lidar com
Task
cancelamento e depois instrumentá-los juntos de várias maneiras para criar fluxos de trabalho complicados.Deixe-me fazer uma pergunta, você já se perguntou qual é o objetivo do token. Não fazia sentido para mim. E então eu li Cancelamento em Threads Gerenciados e tudo ficou claro.
Acredito que o Projeto de Estrutura de Cancelamento no TPL é absolutamente de primeira.
fonte
CancellationTokenSource
pode realmente iniciar a solicitação de cancelamento no token associado (o token não pode fazê-lo por si só): O CancellationToken possui este construtor interno:internal CancellationToken(CancellationTokenSource source) { this.m_source = source; }
e esta propriedade:public bool IsCancellationRequested { get { return this.m_source != null && this.m_source.IsCancellationRequested; } }
O CancellationTokenSource usa o construtor interno, portanto, o token faz referência ao fonte (m_source)Eles são separados não por razões técnicas, mas semânticas. Se você observar a implementação do
CancellationToken
ILSpy, verá que é apenas um invólucroCancellationTokenSource
(e, portanto, não é diferente em termos de desempenho do que passar uma referência).Eles fornecem essa separação de funcionalidade para tornar as coisas mais previsíveis: quando você passa um método a
CancellationToken
, sabe que ainda é o único que pode cancelá-lo. Certamente, o método ainda pode gerar umTaskCancelledException
, mas oCancellationToken
próprio - e quaisquer outros métodos que façam referência ao mesmo token - permanecerão seguros.fonte
CancellationTokenSource
. Você pensaria que poderia simplesmente dizer "não faça isso", mas as pessoas (inclusive eu!) Ocasionalmente fazem essas coisas de qualquer maneira para obter alguma funcionalidade oculta, e isso aconteceria. Essa é a minha teoria atual, pelo menos.O
CancellationToken
é uma estrutura que muitas cópias podem existir devido à transmissão aos métodos.Os
CancellationTokenSource
conjuntos do estado de todos os exemplares de um símbolo quando chamandoCancel
a fonte.Veja esta página do MSDNO motivo do design pode ser apenas uma questão de separação de preocupações e a velocidade de uma estrutura.
fonte
A
CancellationTokenSource
é a 'coisa' que emite o cancelamento, por qualquer motivo. Ele precisa de uma maneira de 'despachar' esse cancelamento para todos osCancellationToken
que foi emitido. É assim que, por exemplo, o ASP.NET pode cancelar operações quando uma solicitação é abortada. Cada solicitação possui umaCancellationTokenSource
que encaminha o cancelamento a todos os tokens emitidos.Isso é ótimo para testes de unidade BTW - crie sua própria fonte de token de cancelamento, obtenha um token, chame
Cancel
a fonte e passe o token para o seu código que precisa lidar com o cancelamento.fonte