【问题标题】:Is Thread.Sleep(Timeout.Infinite); more efficient than while(true){}?是 Thread.Sleep(Timeout.Infinite);比 while(true){} 更高效?
【发布时间】:2012-11-04 13:53:46
【问题描述】:

我有一个控制台应用程序,我希望在监听事件的同时始终保持打开状态。我已经测试了Thread.Sleep(Timeout.Infinite);while (true) { } 并且都允许在保持控制台应用程序打开的同时引发事件。有一个我应该使用的吗?如果线程在休眠,有什么我不应该做的,比如修改类范围内声明的静态集合?

【问题讨论】:

    标签: c# console-application


    【解决方案1】:

    C# 7.1 开始,您可以使 Main 方法异步。这意味着您可以将 Main 方法作为 Task 异步挂起,而不是使用忙等待或线程锁定睡眠。这样,运行 Main 的线程就不会被锁定。并且使用Cts.Cancel(),您可以轻松释放主任务以退出应用程序(不允许其他线程/任务完成工作)。

    static readonly CancellationTokenSource Cts = new CancellationTokenSource();
    static async Task Main(string[] args)
    {
        /* your code here */
    
        // Task running Main is efficiently suspended (no CPU use) forever until Cts activated with Program.Cts.Cancel(); (thread-safe) from anywhere.
        await Task.Delay(Timeout.Infinite, Cts.Token).ConfigureAwait(false);
    }
    

    2020 年,C# 9.0 以来,整个Program.cs 文件内容实际上可以像这样:

    using System.Threading;
    using System.Threading.Tasks;
    
    CancellationTokenSource Cts = new();
    
    /* your code here */
    
    // Task running Main is efficiently suspended (no CPU use) forever until
    // Cts activated with Program.Cts.Cancel(); (thread-safe) from anywhere.
    await Task.Delay(Timeout.Infinite, Cts.Token).ConfigureAwait(false);
    

    如果无限睡眠取消是该案例的正常代码流,您可以尝试捕获 TaskCanceledException(当您尝试等待/与其他任务同步时,请不要使用 Task.Delayinf-sleep , try-catch 效率较低,会混淆队友;使用其他机制进行任务合作):

    try
    {
        await Task.Delay(Timeout.Infinite, Cts.Token).ConfigureAwait(false);
    }
    catch(TaskCanceledException) { /* Ignored */ }
    

    【讨论】:

    【解决方案2】:

    调用 Thread.Sleep 方法会导致当前线程在您传递给该方法的毫秒数或时间间隔内立即阻塞,并将其时间片的剩余部分交给另一个线程。

    https://docs.microsoft.com/en-us/dotnet/standard/threading/pausing-and-resuming-threads?redirectedfrom=MSDN#the-threadsleep-method

    【讨论】:

      【解决方案3】:

      是的while(true) 消耗 CPU,而 sleep() 以更智能的方式工作: sleep() 函数使当前执行上下文进入休眠状态;它通过调用系统调用来自动调用内核睡眠函数
      (a) 设置唤醒定时器
      (b) 将当前进程标记为休眠
      (c) 等待直到唤醒定时器触发或发生中断

      如果你调用sleep(),CPU 可以做其他工作。

      这就是sleep() 有用的原因之一。

      一个有用的链接 - Be careful when using Sleep

      【讨论】:

        【解决方案4】:

        我认为电话

        while (true) { ... } 
        

        是计算密集型的,因为线程永远不会停止,whearas

        Thread.Sleep(Timeout.Infinite);
        

        实际上是在 OS 本机调度程序的帮助下让线程进入睡眠状态。然后线程实际上停止了,所以我想它对计算的要求不那么高。

        【讨论】:

        • 您的回答似乎不确定...“我认为”、“我想”等。
        • 我很确定答案,但我刚开始从事 Java 职业,这就是为什么我使用“我认为”、“我认为”作为预防措施。 :)
        【解决方案5】:

        while(true)... 不同,Thread.Sleep 不使用 CPU 周期,因此从这个意义上说,睡眠效率更高。一般来说,强烈建议不要在spinlocks 之外使用Busy Waiting

        如果线程正在休眠,有什么我不应该做的吗?

        由于您的线程在进入 Thread.Sleep 时被阻止,因此您希望对其资源做的任何事情都是公平的游戏。

        【讨论】:

        • 谢谢,我看到有人向尝试完成我正在做的事情推荐了 while 循环,但我不确定这是否是正确的方法。
        【解决方案6】:

        我建议使用ManualResetEvent(或其他WaitHandle),并致电ManualResetEvent.WaitOne

        这将产生与永远睡眠类似的效果,除了它为您提供了一种在需要时退出无限“块”的干净方式(通过在事件上调用Set())。

        使用while(true) 会消耗CPU 周期,所以绝对要避免。

        有什么我不应该做的,比如修改类范围内声明的静态集合?

        一般来说,不会。由于您的线程将被阻止,因此使用共享数据不应该有任何同步问题(前提是集合中的项目没有特定要求,例如必须在具有适当同步上下文的线程上使用的用户界面元素.)

        【讨论】:

        • 我实际上根本不希望它退出,但我会接受你的回答,以防有人希望有办法退出。
        • @Daniel 很高兴有一种干净的方式让您的程序最终关闭,即使这不是您在任何特定时间点想要的东西;)话虽如此,我个人还是会在睡觉时使用这种方法。
        • 如果程序关闭的唯一时间是机器重新启动时,使用 ManualResetEvent 与 Thread.Sleep() 有何不同?
        • @Daniel 我认为意图更加明确。它会产生同样的效果
        • ManualResetEvent 选项需要更多的字符,其意图并不比“无限期睡眠”更清晰,但它确实感觉不像黑客攻击。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-09-12
        • 2014-10-15
        • 2020-04-08
        • 1970-01-01
        • 2010-10-15
        • 1970-01-01
        • 2020-10-24
        相关资源
        最近更新 更多