Estoy buscando una razón de por qué CancellationToken
se introdujo .NET struct además de la CancellationTokenSource
clase. Entiendo cómo se utilizará la API, pero también quiero entender por qué está diseñada de esa manera.
Es decir, ¿por qué tenemos:
var cts = new CancellationTokenSource();
SomeCancellableOperation(cts.Token);
...
public void SomeCancellableOperation(CancellationToken token) {
...
token.ThrowIfCancellationRequested();
...
}
en lugar de pasar directamente CancellationTokenSource
como:
var cts = new CancellationTokenSource();
SomeCancellableOperation(cts);
...
public void SomeCancellableOperation(CancellationTokenSource cts) {
...
cts.ThrowIfCancellationRequested();
...
}
¿Es esta una optimización del rendimiento basada en el hecho de que las verificaciones del estado de cancelación ocurren con más frecuencia que pasar el token?
Entonces, ¿ CancellationTokenSource
puede realizar un seguimiento y actualizar CancellationTokens
, y para cada token la verificación de cancelación es un acceso de campo local?
Dado que un bool volátil sin bloqueo es suficiente en ambos casos, todavía no puedo ver por qué eso sería más rápido.
¡Gracias!
fuente
Tenía la pregunta exacta y quería entender la lógica detrás de este diseño.
La respuesta aceptada obtuvo la justificación correcta. Aquí está la confirmación del equipo que diseñó esta función (el énfasis es mío):
Enlace: Marco de cancelación de .NET 4
En mi opinión, el hecho de que
CancellationToken
solo pueda observar el estado y no cambiarlo es extremadamente crítico. Puede repartir la ficha como un caramelo y nunca preocuparse de que otra persona, aparte de usted, la cancele. Te protege del código hostil de terceros. Sí, las posibilidades son escasas, pero personalmente me gusta esa garantía.También siento que hace que la API sea más limpia y evita errores accidentales y promueve un mejor diseño de componentes.
Veamos la API pública para ambas clases.
Si los combinara, al escribir LongRunningFunction, veré métodos como esas sobrecargas múltiples de 'Cancelar' que no debería usar. Personalmente, odio ver el método Dispose también.
Creo que el diseño de clase actual sigue la filosofía del 'pozo de éxito', guía a los desarrolladores para crear mejores componentes que puedan manejar la
Task
cancelación y luego instrumentarlos juntos de numerosas maneras para crear flujos de trabajo complicados.Déjame hacerte una pregunta, ¿te has preguntado cuál es el propósito del token? No tenía sentido para mí. Y luego leí Cancelación en hilos gestionados y todo se volvió claro como el cristal.
Creo que el diseño del marco de cancelación en TPL es absolutamente de primera categoría.
fuente
CancellationTokenSource
puede realmente iniciar la solicitud de cancelación en su token asociado (el token no puede hacerlo solo): CancellationToken tiene este constructor interno:internal CancellationToken(CancellationTokenSource source) { this.m_source = source; }
y esta propiedad:public bool IsCancellationRequested { get { return this.m_source != null && this.m_source.IsCancellationRequested; } }
CancellationTokenSource usa el constructor interno, por lo que el token tiene referencia al fuente (m_source)Están separados no por razones técnicas sino semánticas. Si observa la implementación de
CancellationToken
bajo ILSpy, encontrará que es simplemente una envolturaCancellationTokenSource
(y, por lo tanto, no es diferente en términos de rendimiento que pasar una referencia).Proporcionan esta separación de funcionalidad para hacer que las cosas sean más predecibles: cuando pasa un método a
CancellationToken
, sabe que sigue siendo el único que puede cancelarlo. Claro, el método aún podría arrojar unTaskCancelledException
, pero elCancellationToken
mismo, y cualquier otro método que haga referencia al mismo token, permanecería seguro.fuente
CancellationTokenSource
. Pensarías que podrías decir "no hagas eso", pero la gente (¡incluyéndome a mí!) De vez en cuando hace estas cosas para obtener alguna funcionalidad oculta, y sucedería. Esa es mi teoría actual, al menos.El
CancellationToken
es una estructura tantas copias podrían existir debido a pasarlo a métodos.Los
CancellationTokenSource
conjuntos del estado de todos los ejemplares de una muestra cuando se llamaCancel
a la fuente. Ver esta página de MSDNLa razón del diseño podría ser solo una cuestión de separación de preocupaciones y la velocidad de una estructura.
fuente
El
CancellationTokenSource
es la "cosa" que emite la cancelación, por cualquier razón. Necesita una forma de "enviar" esa cancelación a todos losCancellationToken
que ha emitido. Así es como, por ejemplo, ASP.NET puede cancelar operaciones cuando se cancela una solicitud. Cada solicitud tiene unCancellationTokenSource
que reenvía la cancelación a todos los tokens que ha emitido.Esto es genial para pruebas unitarias, por cierto: cree su propio token de cancelación, obtenga un token, llame
Cancel
al origen y pase el token a su código que tiene que manejar la cancelación.fuente