获取任务的CancellationToken(Get Task CancellationToken)

2019-08-17 15:23发布

我能得到CancellationToken被传递给Task执行任务操作过程中构造。 大多数样品的这个样子:

CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;

Task myTask = Task.Factory.StartNew(() =>
{
    for (...)
    {
        token.ThrowIfCancellationRequested();

        // Body of for loop.
    }
}, token);

但是,如果我的行为不是什么拉姆达但在方法放在其他类和我没有直接访问token ? 是唯一的办法是通过token的状态?

Answer 1:

但是,如果我的行为不是什么拉姆达但在方法放在其他类和我没有直接访问令牌? 是唯一的办法是通过令牌状态?

是的,在这种情况下,你就需要通过令牌盒装的状态,或者包含在你的状态下使用一些其他类型。

这个,如果你打算使用时,才需要CancellationToken的方法中,但是。 例如,如果您需要调用token.ThrowIfCancellationRequested()

如果你只使用令牌,以防止被调度的方法,那么它不是必需的。



Answer 2:

我能得到这是在执行任务过程中的行动传递给任务建构的CancellationToken?

不,你不能直接从得到它的Task对象,没有。

但是,如果我的行为不是什么拉姆达但在方法放在其他类和我没有直接访问令牌? 是唯一的办法是通过令牌状态?

这是两个选项,是的。 还有其他的虽然。 (可能不是一个包容性的列表)。

  1. 您可以关闭在取消标记在一个匿名方法

  2. 你可以把它当作状态

  3. 您可以确保用于任务的委托实例具有保持到取消标记一个实例字段,或持有到一些对象持有到令牌等

  4. 您可以公开令牌而其他一些更大范围内的状态,即作为公共静态字段(不好的做法,在大多数情况下,但它有可能适用)



Answer 3:

至于其他的答案中的状态,您可以通过令牌作为参数传递给你的方法。 但是,要记住,你仍然想要将它传递给重要的Task也是如此。 Task.Factory.StartNew( () => YourMethod(token), token) ,例如。

这保证了:

  1. Task如果之前发生的取消将不会运行Task执行(这是一个很好的优化)

  2. 一个OperationCanceledException被调用方法抛出正确转换任务的Canceled状态



Answer 4:

有一个很简单的解决方案:

    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();
    }
}


Answer 5:

您可以通过访问内部字段与反思得到的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 。



Answer 6:

这似乎工作:

public static CancellationToken GetCancellationToken(this Task task)
{
  return new TaskCanceledException(task).CancellationToken;
}

这可能是必要的,使通用的任务助手保留已取消的任务的的CancellationToken(我来到这里,同时试图让乔恩斯基特的WithAllExceptions方法保存令牌)。



Answer 7:

当我们看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<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;
            }
        }
    }


文章来源: Get Task CancellationToken