Je cherche une raison pour laquelle .NET CancellationToken
struct a été introduit en plus de la CancellationTokenSource
classe. Je comprends comment l'API doit être utilisée, mais je veux également comprendre pourquoi elle est conçue de cette façon.
Ie, pourquoi avons-nous:
var cts = new CancellationTokenSource();
SomeCancellableOperation(cts.Token);
...
public void SomeCancellableOperation(CancellationToken token) {
...
token.ThrowIfCancellationRequested();
...
}
au lieu de passer directement CancellationTokenSource
comme:
var cts = new CancellationTokenSource();
SomeCancellableOperation(cts);
...
public void SomeCancellableOperation(CancellationTokenSource cts) {
...
cts.ThrowIfCancellationRequested();
...
}
S'agit-il d'une optimisation des performances basée sur le fait que les vérifications d'état d'annulation se produisent plus fréquemment que la transmission du jeton?
Pour que cela CancellationTokenSource
puisse suivre et mettre à jour CancellationTokens
, et pour chaque jeton, le contrôle d'annulation est un accès au champ local?
Étant donné qu'un bool volatile sans verrouillage est suffisant dans les deux cas, je ne vois toujours pas pourquoi ce serait plus rapide.
Merci!
la source
J'avais la question exacte et je voulais comprendre la raison d'être de cette conception.
La réponse acceptée a parfaitement justifié la justification. Voici la confirmation de l'équipe qui a conçu cette fonctionnalité (c'est moi qui souligne):
Lien: Framework d'annulation .NET 4
À mon avis, le fait que l'
CancellationToken
on ne puisse qu'observer l'état et ne pas le changer est extrêmement critique. Vous pouvez distribuer le jeton comme un bonbon et ne jamais craindre que quelqu'un d'autre, autre que vous, l'annule. Il vous protège du code tiers hostile. Oui, les chances sont minces, mais j'aime personnellement cette garantie.Je pense également que cela rend l'API plus propre et évite les erreurs accidentelles et favorise une meilleure conception des composants.
Regardons l'API publique pour ces deux classes.
Si vous deviez les combiner, lors de l'écriture de LongRunningFunction, je verrai des méthodes comme ces multiples surcharges de 'Cancel' que je ne devrais pas utiliser. Personnellement, je déteste aussi voir la méthode Dispose.
Je pense que la conception actuelle de la classe suit la philosophie du «gouffre du succès», elle guide les développeurs pour créer de meilleurs composants capables de gérer l'
Task
annulation, puis les instrumenter ensemble de nombreuses manières pour créer des flux de travail compliqués.Permettez-moi de vous poser une question, vous êtes-vous demandé à quoi sert le jeton. Cela n'avait aucun sens pour moi. Et puis j'ai lu Annulation dans Managed Threads et tout est devenu limpide.
Je crois que la conception du cadre d'annulation dans TPL est absolument de premier ordre.
la source
CancellationTokenSource
peut réellement lancer la demande d'annulation sur son jeton associé (le jeton ne peut pas le faire par lui-même): Le CancellationToken a ce constructeur interne:internal CancellationToken(CancellationTokenSource source) { this.m_source = source; }
et cette propriété:public bool IsCancellationRequested { get { return this.m_source != null && this.m_source.IsCancellationRequested; } }
Le CancellationTokenSource utilise le constructeur interne, donc le jeton fait référence au source (m_source)Ils ne sont pas séparés pour des raisons techniques mais sémantiques. Si vous regardez l'implémentation de
CancellationToken
under ILSpy, vous constaterez que c'est simplement un wrapperCancellationTokenSource
(et donc pas différent en termes de performances que de passer une référence).Ils fournissent cette séparation des fonctionnalités pour rendre les choses plus prévisibles: lorsque vous passez une méthode a
CancellationToken
, vous savez que vous êtes toujours le seul à pouvoir l'annuler. Bien sûr, la méthode pourrait toujours lancer unTaskCancelledException
, maisCancellationToken
elle-même - et toute autre méthode référençant le même jeton - resterait sûre.la source
CancellationTokenSource
. On pourrait penser que vous pourriez simplement dire "ne faites pas ça", mais les gens (y compris moi!) Font parfois ces choses de toute façon pour accéder à une fonctionnalité cachée, et cela arriverait. C'est du moins ma théorie actuelle.Le
CancellationToken
est une structure tellement de copies pourraient exister en raison de sa transmission aux méthodes.Les
CancellationTokenSource
définit l'état de toutes les copies d'un jeton lors de l' appelCancel
à la source. Voir cette page MSDNLa raison de la conception pourrait être juste une question de séparation des préoccupations et de la vitesse d'une structure.
la source
C'est
CancellationTokenSource
la «chose» qui émet l'annulation, quelle qu'en soit la raison. Il a besoin d 'un moyen d' «envoyer» cette annulation à tous lesCancellationToken
messages émis. C'est ainsi que, par exemple, ASP.NET peut annuler des opérations lorsqu'une demande est abandonnée. Chaque demande a unCancellationTokenSource
qui transmet l'annulation à tous les jetons qu'elle a émis.C'est idéal pour les tests unitaires BTW - créez votre propre source de jeton d'annulation, obtenez un jeton, appelez
Cancel
la source et transmettez le jeton à votre code qui doit gérer l'annulation.la source