CancellationToken 不能使用零超时

Posted

技术标签:

【中文标题】CancellationToken 不能使用零超时【英文标题】:CancellationToken not working with zero timeout 【发布时间】:2021-12-27 05:44:29 【问题描述】:

我有一个代码,它依赖于一个零超时的取消令牌来提早退出。这是sn-p

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 块,但它没有这样做。知道为什么会这样吗?另外,有什么方法可以实现我想要做的吗?

【问题讨论】:

【参考方案1】:

解释问题:

为什么,当您从一个 CancellationTokenSource 注册了 timeout,然后将生成的 token source 设置为 timeout 为零,它不知道吗 应该取消吗?

更多解释:

毕竟,零就是零,它应该知道它被取消了。

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

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

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

【讨论】:

感谢您的全面回答。检查取消令牌的代码由另一方拥有,这就是为什么我试图通过通过零超时来提前退出。我会尝试重新考虑这个问题,因为那行不通。【参考方案2】:

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

【讨论】:

以上是关于CancellationToken 不能使用零超时的主要内容,如果未能解决你的问题,请参考以下文章

具有CancellationToken和ReadTimeout的异步串行端口

区分用户取消超时

我应该如何使用 DataflowBlockOptions.CancellationToken?

如何在 C# 中等待单个事件,带有超时和取消

如何使用 CancellationToken 属性?

C#:使用 CancellationToken 取消 MySqlCommand,给出 NULLReferenceException