CancellationToken 不适用于零超时

CancellationToken not working with zero timeout

我有一个代码依赖于零超时的取消令牌来提早退出。这是片段

using System;
using System.Threading;

namespace ConsoleApp2
{
    class Program
    {
        void DoIdleWait(TimeSpan timeout, CancellationToken cancellationToken)
        {
            var linkedCancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
            linkedCancellationTokenSource.CancelAfter(timeout);

            while (!linkedCancellationTokenSource.IsCancellationRequested)
            {
                Console.WriteLine("Waiting");
            }
        }

        static void Main(string[] args)
        {
            var prog = new Program();
            var cts = new CancellationTokenSource();
            cts.CancelAfter(TimeSpan.FromSeconds(2));
            prog.DoIdleWait(TimeSpan.Zero, cts.Token);
        }
    }
}

由于超时为零,我希望它不会进入 if 块,但它并没有这样做。知道为什么会这样吗?另外,有什么方法可以实现我想要做的事情吗?

解释一下问题:

Why, when you create a linked CancellationTokenSource from a CancellationTokenSource with a registered timeout, then set the resultant token source with a timeout of zero, does it not know it should be cancelled?

更多释义:

After all, zero is zero and it should know it's cancelled.

答案是,因为 CancellationTokenSource.CancelAfter 试图注册一个计时器回调,它试图将分辨率设置为零毫秒,这是它不可能遵守的。

因此,在不使用 CPU 旋转的情况下,您可以获得任何标准计时机制可以为您提供的最小分辨率,大约 5+ 毫秒。

您的问题可能有其他解决方案,但是,简而言之,您不能依赖 0 第二次超时通过 IsCancellationRequested 立即确认。你需要重新考虑你的问题。

CancellationTokenSource.CancelAfter method (source code) 不包括对 TimeSpan.Zero 值的特殊处理,因此取消计划到 ThreadPoolTimer 超时为零。这会导致竞争条件,同步 while (!linkedCancellationTokenSource.IsCancellationRequested) 循环在大多数情况下获胜。考虑到 DoIdleWait 方法的实现不受您的控制,我能想到的唯一解决方案是,如果您知道超时为零,则不要调用此方法。