stop - how to use cancellationtoken c#
VinculaciĆ³n de tokens de cancelaciĆ³n (2)
Utilizo un token de cancelación que se transmite para que mi servicio pueda cerrarse limpiamente. El servicio tiene una lógica que sigue intentando conectarse a otros servicios, por lo que el token es una buena manera de romper estos bucles de reintento que se ejecutan en hilos separados. Mi problema es que necesito hacer una llamada a un servicio que tenga una lógica interna de reintento, pero que vuelva después de un período establecido si falla un reintento. Me gustaría crear un nuevo token de cancelación con un tiempo de espera que haga esto por mí. El problema con esto es que mi nuevo token no está vinculado al token "maestro", por lo que cuando se cancela el token maestro, mi nuevo token seguirá activo hasta que se agote el tiempo de espera o se realice la conexión y se devuelva. Lo que me gustaría hacer es vincular los dos tokens para que cuando se cancele el maestro, mi nuevo también se cancele. Intenté usar el método CancellationTokenSource.CreateLinkedTokenSource
pero cuando se agotó el tiempo de espera de mi nuevo token, también se canceló el token maestro. ¿Hay alguna forma de hacer lo que necesito hacer con los tokens o requerirá cambios en la lógica de reintento (probablemente no pueda hacerlo fácilmente)?
Esto es lo que quiero hacer:
Master Token: pasó por varias funciones para que el servicio pueda cerrarse limpiamente. Token temporal: se pasa a una sola función y se establece en tiempo de espera después de un minuto
Si se cancela el token maestro, también se debe cancelar el token temporal.
Cuando el token temporal caduca, NO debe cancelar el token maestro.
Como share , puedes hacer esto con CancellationTokenSource.CreateLinkedTokenSource()
. Quiero intentar mostrar un patrón de cómo usar un token de este tipo cuando desea distinguir entre la cancelación de una tarea general frente a la cancelación de una tarea secundaria sin cancelar la tarea general.
async Task MyAsyncTask(
CancellationToken ct)
{
// Keep retrying until the master process is cancelled.
while (true)
{
// Ensure we cancel ourselves if the parent is cancelled.
ct.ThrowIfCancellationRequested();
var childCts = CancellationTokenSource.CreateLinkedTokenSource(ct);
// Set a timeout because sometimes stuff gets stuck.
childCts.CancelAfter(TimeSpan.FromSeconds(32));
try
{
await DoSomethingAsync(childCts.Token);
}
// If our attempt timed out, catch so that our retry loop continues.
// Note: because the token is linked, the parent token may have been
// cancelled. We check this at the beginning of the while loop.
catch (OperationCancelledException) when (childCts.IsCancellationRequested)
{
}
}
}
Cuando el token temporal caduca, NO debe cancelar el token maestro.
Tenga en cuenta que la MyAsyncTask()
acepta CancellationToken
lugar de CancellationTokenSource
. Como el método solo tiene acceso a los miembros en CancellationToken
, no puede cancelar accidentalmente el token maestro / padre. Le recomiendo que organice su código de manera que el código de CancellationTokenSource
de la tarea maestra sea visible con el menor código posible. En la mayoría de los casos, esto se puede hacer pasando un método CancellationTokenSource.Token
a los métodos en lugar de compartir la referencia a la CancellationTokenSource
.
No lo he investigado, pero puede haber una manera con algo como la reflexión para cancelar por la fuerza un CancellationToken
sin acceso a su CancellationTokenSource
. Esperemos que sea imposible, pero si fuera posible, sería considerado una mala práctica y no es algo de qué preocuparse en general.
Desea utilizar CancellationTokenSource.CreateLinkedTokenSource
. Permite tener un "padre" y un "hijo" CancellationTokenSource
. Aquí hay un ejemplo simple:
var parentCts = new CancellationTokenSource();
var childCts = CancellationTokenSource.CreateLinkedTokenSource(parentCts.Token);
childCts.CancelAfter(1000);
Console.WriteLine("Cancel child CTS");
Thread.Sleep(2000);
Console.WriteLine("Child CTS: {0}", childCts.IsCancellationRequested);
Console.WriteLine("Parent CTS: {0}", parentCts.IsCancellationRequested);
Console.WriteLine();
parentCts.Cancel();
Console.WriteLine("Cancel parent CTS");
Console.WriteLine("Child CTS: {0}", childCts.IsCancellationRequested);
Console.WriteLine("Parent CTS: {0}", parentCts.IsCancellationRequested);
Salida como se esperaba:
Cancelar niño CTS
Niño CTS: Verdadero
Padre CTS: FalsoCancelar padre CTS
Niño CTS: Verdadero
Padre CTS: Verdadero