Usando CancellationToken en C#
CancellationToken es una estructura (struct) de .NET que no hace otra cosa que indicar o notificar que las operaciones en curso deberían cancelarse.
Esta estructura pertenece al namespace System.Threading y podemos asociarla con la clase CancellationTokenSource para obtener un token que nos permita asociar una tarea o proceso con CancellationToken.
CancellationToken tiene por su parte una serie de propiedades que podemos utilizar, como por ejemplo para preguntar si un determinado token puede ser cancelado o no (CanBeCanceled), o si ya se ha llamado al token para una petición de cancelación (IsCancellationRequested).
Podemos acceder a más información sobre CancellationToken en este enlace.
La clase CancellationTokenSource por su parte, pertenece también al namespace System.Threading y su cometido es el de indicar a CancellationToken que debería ser cancelado.
Esta clase es además, una clase de tipo IDisposable.
Podemos acceder a más información sobre CancellationTokenSource en este enlace.
Tanto System.Threading.Tasks.Task como System.Threading.Tasks.Task<TResult>, soportan la cancelación a través de tokens de cancelación.
Para que todo esto tenga sentido y pueda funcionar tal y como esperamos, debemos crear una tarea que sea cancelable.
Deberemos pasar un token de cancelación a un delegado o a una instancia de la tarea a ejecutar.
Y lógicamente, llamar a la acción de cancelación una vez que el usuario o un proceso, desee cancelar una determinada acción.
Veamos esto con un par de ejemplos muy similares.
El primero de ellos con una tarea ejecutada dentro del propio método Main de la aplicación de consola:
using System; using System.Threading; using System.Threading.Tasks; namespace CancellationTasks { public class Program { private static int counter = 0; public static void Main(string[] args) { Console.WriteLine("Started"); Console.WriteLine(); CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); CancellationToken cancellationToken = cancellationTokenSource.Token; Task task = Task.Run(() => { while (!cancellationToken.IsCancellationRequested) { counter++; Console.Write($"{counter}|"); Thread.Sleep(500); } }, cancellationToken); Console.WriteLine("Press enter to stop the task"); Console.ReadLine(); cancellationTokenSource.Cancel(); Console.WriteLine($"Task executed {counter} times"); Console.WriteLine(); Console.WriteLine("Press any key to close"); Console.ReadKey(); } } }
El segundo de ellos con una llamada a un método que se lanzará de manera similar a la anterior:
using System; using System.Threading; using System.Threading.Tasks; namespace CancellationTasks { public class Program { private static int counter = 0; public static void Main(string[] args) { Console.WriteLine("Started"); Console.WriteLine(); CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); CancellationToken cancellationToken = cancellationTokenSource.Token; //Task task = Task.Run(() => //{ // while (!cancellationToken.IsCancellationRequested) // { // counter++; // Console.Write($"{counter}|"); // Thread.Sleep(500); // } //}, cancellationToken); Task task = Task.Run(() => Process(cancellationToken)); Console.WriteLine("Press enter to stop the task"); Console.ReadLine(); cancellationTokenSource.Cancel(); Console.WriteLine($"Task executed {counter} times"); Console.WriteLine(); Console.WriteLine("Press any key to close"); Console.ReadKey(); } private static void Process(CancellationToken cancellationToken) { while (true) { if (cancellationToken.IsCancellationRequested) { return; } counter++; Console.Write($"{counter}|"); Thread.Sleep(500); } } } }
Tómese estos ejemplos como ejemplos de demostración para demostrar el funcionamiento de CancellationToken.
Una cosa que debemos tener en cuenta es que una vez que se cancela una tarea, la propiedad IsCancellationRequested nos devolverá siempre true, ya que es una propiedad de sólo lectura, por lo que para reutilizar CancellationToken, deberíamos volver a crear un objeto de tipo CancellationTokenSource para luego asignarle a CancellationToken un nuevo token.
Es decir, cada tarea que deseemos procesar y en su caso cancelar, debe llevar su propio token.
Entendamos que CancellationToken nos va a ofrecer características que de otra manera no obtendríamos respecto a la cancelación de tareas.
De hecho, todos los procesos de sincronización y estabilización los delegaremos en CancellationToken, evitándonos muchos problemas.
Como información extra a esta entrada, te sugiero leer la información sobre Task Cancellation que encontrarás en este enlace, y la de Cancellation in Managed Threads en este otro enlace.
¡Happy Coding!
3 Responsesso far
HOla amigo, me funciono a la perfeccion tu ejempplo muchisimas gracias, ahora tengo un porblema y espero puedas ayudarme, resulta que al cancelar el token (la tarea) , no hay manera quee vuelva a funcionar, asi que debo cerrar y abrir la aplicacion de nuevo, como puedo lograr que al cancelarlo pueda volver a utilizarla ? espero puedas ayudarme a resolver esto porfavor.
¿Podrías poner algo de código para verlo?.
Por ejemplo, en GitHub.
Un saludo,
Jorge
declaro esto primero
static CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
public static CancellationToken cancellationToken = cancellationTokenSource.Token;
el metodo como esta creado
public static async System.Threading.Tasks.Task videoDescarga(System.Threading.CancellationToken cancellationToken)
la linea de codigo donde uso el token
await client.DownloadMediaStreamAsync(streamInfo, fileName, progress, cancellationToken);
el metodo para cancelarlo
public static void cerrarVideo()
{
cancellationTokenSource.Cancel();
}
mensionas que se debe crear otro cancellationTokenSource , como debo hacer eso ¿?