remarks generate example c# .net multithreading cancellation-token

c# - generate - Obtener la cancelación de la tareaToken



params comments c# (6)

¿Puedo obtener CancelToken que se pasó al constructor de tareas durante la ejecución de la tarea?

No, no puede obtenerlo directamente del objeto Task , no.

Pero, ¿qué pasa si mi acción no es lambda sino un método colocado en otra clase y no tengo acceso directo al token? ¿La única forma es pasar el token como estado?

Esas son dos de las opciones, sí. Aunque hay otros (Posiblemente no sea una lista inclusiva).

  1. Puede cerrar el token de cancelación en un método anónimo

  2. Puedes pasarlo como estado

  3. Puede asegurarse de que la instancia utilizada para el delegado de la tarea tenga un campo de instancia que se mantenga en el token de cancelación, o se mantenga en algún objeto que se mantenga en el token, etc.

  4. Puede exponer el token a través de otro alcance mayor como estado, es decir, como un campo estático público (mala práctica en la mayoría de los casos, pero en ocasiones podría ser aplicable)

¿Puedo obtener CancellationToken que se pasó al constructor de Task durante la ejecución de la tarea? La mayoría de las muestras se ven así:

CancellationTokenSource cts = new CancellationTokenSource(); CancellationToken token = cts.Token; Task myTask = Task.Factory.StartNew(() => { for (...) { token.ThrowIfCancellationRequested(); // Body of for loop. } }, token);

Pero, ¿qué pasa si mi acción no es lambda sino un método colocado en otra clase y no tengo acceso directo al token ? ¿La única forma es pasar el token como estado?


Pero, ¿qué pasa si mi acción no es lambda sino un método colocado en otra clase y no tengo acceso directo al token? ¿La única forma es pasar el token como estado?

Sí, en ese caso, necesitaría pasar el token en caja como estado, o incluido en algún otro tipo que use como estado.

Sin embargo, esto solo es necesario si planea usar el CancellationToken dentro del método. Por ejemplo, si necesita llamar a token.ThrowIfCancellationRequested() .

Si solo está utilizando el token para evitar que se programe el método, no es necesario.


Como lo indican otras respuestas, puede pasar el token como parámetro a su método. Sin embargo, es importante recordar que también desea pasarlo a la Task . Task.Factory.StartNew( () => YourMethod(token), token) , por ejemplo.

Esto asegura que:

  1. La Task no se ejecutará si la cancelación se produce antes de que se ejecute la Task (esta es una buena optimización)

  2. Una OperationCanceledException lanzada por el método llamado transita correctamente la Tarea a un estado Canceled


Cuando miramos el código fuente de referencia de la clase de Tarea, podemos ver que el token de cancelación se almacena dentro de una clase interna: ContingentProperties

https://referencesource.microsoft.com/#mscorlib/system/threading/Tasks/Task.cs,90a9f91ddd80b5cc

El propósito es evitar el acceso a estas propiedades y esas propiedades no siempre son necesarias.

internal class ContingentProperties { // Additional context internal ExecutionContext m_capturedContext; // The execution context to run the task within, if any. // Completion fields (exceptions and event) internal volatile ManualResetEventSlim m_completionEvent; // Lazily created if waiting is required. internal volatile TaskExceptionHolder m_exceptionsHolder; // Tracks exceptions, if any have occurred // Cancellation fields (token, registration, and internally requested) internal CancellationToken m_cancellationToken; // Task''s cancellation token, if it has one internal Shared<CancellationTokenRegistration> m_cancellationRegistration; // Task''s registration with the cancellation token internal volatile int m_internalCancellationRequested; // Its own field because threads legally ---- to set it. // Parenting fields // # of active children + 1 (for this task itself). // Used for ensuring all children are done before this task can complete // The extra count helps prevent the ---- for executing the final state transition // (i.e. whether the last child or this task itself should call FinishStageTwo()) internal volatile int m_completionCountdown = 1; // A list of child tasks that threw an exception (TCEs don''t count), // but haven''t yet been waited on by the parent, lazily initialized. internal volatile List<Task> m_exceptionalChildren; /// <summary> /// Sets the internal completion event. /// </summary> internal void SetCompleted() { var mres = m_completionEvent; if (mres != null) mres.Set(); } /// <summary> /// Checks if we registered a CT callback during construction, and deregisters it. /// This should be called when we know the registration isn''t useful anymore. Specifically from Finish() if the task has completed /// successfully or with an exception. /// </summary> internal void DeregisterCancellationCallback() { if (m_cancellationRegistration != null) { // Harden against ODEs thrown from disposing of the CTR. // Since the task has already been put into a final state by the time this // is called, all we can do here is suppress the exception. try { m_cancellationRegistration.Value.Dispose(); } catch (ObjectDisposedException) { } m_cancellationRegistration = null; } } }


Hay una solución muy simple:

class CancelingTasks { private static void Foo(CancellationToken token) { while (true) { token.ThrowIfCancellationRequested(); Thread.Sleep(100); Console.Write("."); } } static void Main(string[] args) { CancellationTokenSource source = new CancellationTokenSource(); CancellationToken tok = source.Token; tok.Register(() => { Console.WriteLine("Cancelled."); }); Task t = new Task(() => { Foo(tok); }, tok); t.Start(); Console.ReadKey(); source.Cancel(); source.Dispose(); Console.WriteLine("Main program done, press any key."); Console.ReadKey(); } }


Puede obtener CancelaciónToken accediendo a los campos internos con una reflexión.

public CancellationToken GetCancellationToken(Task task) { object m_contingentProperties = task .GetType() .GetField("m_contingentProperties", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance) .GetValue(task); object m_cancellationToken = m_contingentProperties .GetType() .GetField("m_cancellationToken", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance) .GetValue(m_contingentProperties); return (CancellationToken)m_cancellationToken; }

Sugerencia: Puede buscar esas cosas por su cuenta con ILSpy .