获取任务取消提示
我可以获取在任务操作执行期间传递给Task
构造函数的CancellationToken
。 大多数样本看起来像这样:
CancellationTokenSource cts = new CancellationTokenSource(); CancellationToken token = cts.Token; Task myTask = Task.Factory.StartNew(() => { for (...) { token.ThrowIfCancellationRequested(); // Body of for loop. } }, token);
但是如果我的动作不是lambda而是放在其他类中的方法而且我没有直接访问token
呢? 唯一的方法是将token
作为状态传递吗?
但是如果我的动作不是lambda而是放在其他类中的方法而且我没有直接访问令牌呢? 唯一的方法是将令牌作为状态传递吗?
是的,在这种情况下,您需要将标记框作为状态传递,或者包含在您用作状态的其他类型中。
但是,只有在计划在方法中使用CancellationToken
时才需CancellationToken
。 例如,如果需要调用token.ThrowIfCancellationRequested()
。
如果您只是使用令牌来阻止该方法被安排,那么它不是必需的。
我可以获得在任务操作执行期间传递给Task构造函数的CancellationToken吗?
不,你不能直接从Task
对象获取它,不。
但是如果我的动作不是lambda而是放在其他类中的方法而且我没有直接访问令牌呢? 唯一的方法是将令牌作为状态传递吗?
这是两个选项,是的。 还有其他人。 (可能不是包容性列表。)
-
您可以使用匿名方法关闭取消令牌
-
你可以把它作为状态传递给你
-
您可以确保用于任务委托的实例具有一个保留在取消令牌上的实例字段,或者保留一些保留在令牌上的对象等。
-
您可以将令牌通过其他更大的范围公开为状态,即作为公共静态字段(在大多数情况下是不好的做法,但有时可能适用)
正如其他答案所述,您可以将令牌作为参数传递给您的方法。 但是,重要的是要记住,您仍然希望将它传递给Task
。 Task.Factory.StartNew( () => YourMethod(token), token)
。
这可以确保:
-
如果在
Task
执行之前发生取消,则Task
将不会运行(这是一个很好的优化) -
被调用方法抛出的
OperationCanceledException
正确地将Task转换为Canceled
状态
有一个非常简单的解决方案:
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(); } }
您可以通过访问带reflection的内部字段来获取CancellationToken。
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; }
提示:您可以使用ILSpy自行搜索此类内容。
当我们查看Task类引用源代码时,我们可以看到取消令牌存储在内部类中:ContingentProperties
https://referencesource.microsoft.com/#mscorlib/system/threading/Tasks/Task.cs,90a9f91ddd80b5cc
目的是避免访问这些属性,并不总是需要这些属性。
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 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 // (ie 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 m_exceptionalChildren; /// /// Sets the internal completion event. /// internal void SetCompleted() { var mres = m_completionEvent; if (mres != null) mres.Set(); } /// /// 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. /// 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; } } }