【问题标题】:What is the impact of Thread.Sleep(1) in C#?C# 中的 Thread.Sleep(1) 有什么影响?
【发布时间】:2009-02-03 17:59:29
【问题描述】:

在 windows 窗体应用程序中调用Thread.Sleep(1) 有什么影响,如下代码所示:

public Constructor()
{
    Thread thread = new Thread(Task);
    thread.IsBackground = true;
    thread.Start();
}

private void Task()
{
    while (true)
    {
        // do something
        Thread.Sleep(1);
    }
}

这个线程会占用所有可用的 CPU 吗?

我可以使用哪些分析技术来测量此线程的 CPU 使用率(任务管理器除外)?

【问题讨论】:

  • 到目前为止的所有答案都表明效果可以忽略不计,但你能否澄清一下你试图用这个来完成什么?
  • 我只是想看看我的 GUI 应用程序是否因为短暂的 1 毫秒延迟而将所有时间都花在了这个线程上。

标签: c# multithreading


【解决方案1】:

如上所述,您的循环不会占用 CPU。

但请注意:Windows 不是实时操作系统,因此您将不会每秒从 Thread.Sleep 获得 1000 次唤醒( 1)。如果您没有使用timeBeginPeriod 设置您的最小分辨率,您将每 15 毫秒唤醒一次。即使您将最小分辨率设置为 1 毫秒,您仍然只会每 3-4 毫秒唤醒一次。

为了获得毫秒级计时器粒度,您必须使用 Win32 多媒体计时器 (C# wrapper)。

【讨论】:

  • 你能举一个在C#中设置timeBeginPeriod的例子吗?
  • pinvoke.net/default.aspx/winmm.timeBeginPeriod 根据 MSDN 文档,timeBeginPeriod 应该与 timeEndPeriod 调用相匹配。另请注意“此功能会影响全局 Windows 设置”。并且会影响系统性能。
【解决方案2】:

不,它不会占用 CPU,它只会暂停你的线程至少那么长时间。当您的线程暂停时,操作系统可以调度另一个不相关的线程来使用处理器。

【讨论】:

  • 不过,这会不必要地占用大量 CPU 时间。
  • @Joel 如何/为什么会花费更多时间而不是一段时间(真的){ i++; } 还是在 CPU 使用率方面基本相同?
  • 在现代 CPU 上,与使用 i++ 相比,1ms 的睡眠是永恒的。
  • @Joel 相对于 10 功能计算器的总可用 CPU 时间,是的,这将占用“很多”CPU 时间。相对于过去十年制造的任何机器的可用 CPU 时间,它将使用大约零 CPU 时间。
【解决方案3】:

Thread.Sleep(1) 如上所述不会占用 CPU。

当线程休眠(或多或少)时会发生以下情况:

  • Thread.Sleep 被转换为系统调用,进而触发陷阱(允许操作系统控制的中断)
  • 操作系统检测到睡眠调用并将您的线程标记为阻塞。
  • 操作系统在内部保留了需要唤醒的线程列表以及唤醒时间。
  • 由于线程不再使用 CPU 操作系统...
  • 如果父进程没有用完其所有时间片,操作系统将安排该进程的另一个线程执行。
  • 否则另一个进程(或空闲进程)将开始执行。
  • 时间到了,你的线程会被重新调度执行,但这并不意味着它会自动开始执行。

最后一点,我不完全知道您在做什么,但您似乎正在尝试扮演调度程序的角色,也就是说,睡觉以为 CPU 提供时间来做其他事情。 .

在少数情况下(确实很少)可能没问题,但大多数情况下你应该让调度程序完成它的工作,它可能比你知道的更多,并且可以让工作比你能做的更好

【讨论】:

    【解决方案4】:

    应避免较短的睡眠时间,因为放弃其时间片的线程在重新发出信号时会获得优先级提升,这可能导致高上下文切换。在多线程/服务器应用程序中,这可能会导致抖动效应,因为线程正在争夺 CPU 时间。相反,请依赖异步函数和同步对象,例如临界区或互斥体/信号量。

    【讨论】:

      【解决方案5】:

      正如 Bob Nadler 提到的,Thread.Sleep(1) 不保证 1 毫秒的睡眠时间。

      这是一个使用 Win32 多媒体计时器强制休眠 1 毫秒的示例。

          [DllImport("winmm.dll")]
          internal static extern uint timeBeginPeriod(uint period);
          [DllImport("winmm.dll")]
          internal static extern uint timeEndPeriod(uint period);
      
          timeBeginPeriod(1);
          while(true)
          {
              Thread.Sleep(1); // will sleep 1ms every time
          }
          timeEndPeriod(1);
      

      在 C# GUI 应用程序中对此进行测试,我发现该应用程序使用了大约 50% 的 CPU。

      有关此主题的更多讨论,请参阅以下论坛主题:

      http://www.dotnet247.com/247reference/msgs/57/289291.aspx

      【讨论】:

        【解决方案6】:

        这是一个旧线程,在我的很多搜索中都会出现,但 Win7 有一个新的调度程序,并且其行为似乎与上述不同。

        using System;
        using System.Collections.Generic;
        using System.Linq;
        using System.Text;
        using System.Threading;
        
        namespace ConsoleApplication2
        {
            class Program
            {
                static void Main(string[] args)
                {
                    DateTime dtEnd = DateTime.Now.AddSeconds(1.0);
                    int i = 0;
                    while (DateTime.Now < dtEnd)
                    {
                        i++;
                        Thread.Sleep(1);
                    }
        
                    Console.WriteLine(i.ToString());
        
                    i = 0;
                    long lStart = DateTime.Now.Ticks;
                    while (i++ < 1000)
                        Thread.Sleep(1);
        
                    long lTmp = (DateTime.Now.Ticks - lStart) / 10000;
        
                    Console.WriteLine(lTmp.ToString());
        
                    Console.Read();
                }
            }
        }
        

        使用上面的代码,我的第一个结果是 946。所以在使用 1ms 睡眠的 1 秒时间跨度内,我得到了 946 次唤醒。这非常接近 1 毫秒。

        第二部分询问以 1ms 的时间完成 1000 个睡眠事件需要多长时间。我得到了 1034 毫秒。同样,将近 1 毫秒。

        这是在 1.8ghz core2duo + Win7 上使用 .Net 4.0

        编辑:记住,sleep(x) 并不意味着在这个时候醒来,它意味着不早于这个时候叫醒我。不能保证。不过,您可以提高线程的优先级,并且 Windows 应该在较低优先级的线程之前安排您的线程。

        【讨论】:

        • 关于 Win 7 调度程序的评论。我运行了 Bengie 的示例“命名空间 ConsoleApplication2”帖子,它报告了 65 和 15600,表示大约 15 毫秒的步骤。这是一个运行 .NET 3.5、Win 7 的双 3.33Mhx。奇怪的是,昨天我的日志中有一些代码显示 sleep(1) 更接近 1ms,就像他的帖子声称的那样。我的结论是我的机器上有一些设置或配置发生了变化,但我无法确定。在带有或不带有调试器的调试或发布模式下,答案相同。我没有使用开始/结束时间段。 ma上的负载很小
        【解决方案7】:

        不,它不会占用所有可用的 CPU,因为当另一个线程有工作要做时,操作系统的调度程序会关闭一个睡眠线程。

        【讨论】:

          【解决方案8】:

          不,不会。你几乎看不到它。大约每秒不到 1000 次,这个线程会在再次入睡之前醒来并且几乎什么都不做。

          编辑:

          我必须检查一下。在 Java 1.5 上运行,此测试

          @Test
          public void testSpeed() throws InterruptedException {
              long currentTime = System.currentTimeMillis();
              int i = 0;
                  while (i < 1000)
                  {
                      Thread.sleep(1);
                      i++;
                  }
              System.out.println("Executed in " + (System.currentTimeMillis() - currentTime));
          }
          

          在我的 3ghz 机器上以每秒大约 500 次睡眠的速度运行。我想C#应该差不多。我假设有人会为这个非常重要的现实世界基准测试报告 C# 数字。顺便说一句,没有可观察到的 CPU 使用率。

          【讨论】:

          • 我怀疑它是否会经常发生:睡眠的参数是最短时间,而上下文切换需要很长时间才能得到很多。
          • 在现代多核系统上,1ms 是一个非常长的时间。我想知道这个循环实际上是如何运行的?
          【解决方案9】:

          一个线程一次最多只能占用一个(逻辑)CPU。并且 1 毫秒的睡眠不会占用太多时间。不要出汗。

          【讨论】:

            【解决方案10】:

            也看看这个:msdn forum

            using System;
            using System.Diagnostics;
            using System.Runtime.InteropServices;
            using System.Threading;
            
            namespace Test
            {
                public static class Program
                {
                    public static void Main(string[] args)
                    {
                        Stopwatch sw = new Stopwatch();
            
                        for (int i = 0; i < 10; ++i)
                        {
                            sw.Reset();
                            sw.Start();
                            Thread.Sleep(50);
                            sw.Stop();
            
                            Console.WriteLine("(default) Slept for " + sw.ElapsedMilliseconds);
            
                            TimeBeginPeriod(1);
                            sw.Reset();
                            sw.Start();
                            Thread.Sleep(50);
                            sw.Stop();
                            TimeEndPeriod(1);
            
                            Console.WriteLine("(highres) Slept for " + sw.ElapsedMilliseconds + "\n");
                        }
                    }
            
                    [DllImport("winmm.dll", EntryPoint="timeBeginPeriod", SetLastError=true)]
                    private static extern uint TimeBeginPeriod(uint uMilliseconds);
            
                    [DllImport("winmm.dll", EntryPoint="timeEndPeriod", SetLastError=true)]
                    private static extern uint TimeEndPeriod(uint uMilliseconds);
                }
            }
            

            【讨论】:

              猜你喜欢
              • 2014-11-26
              • 1970-01-01
              • 2013-05-11
              • 2022-12-31
              • 2013-03-07
              • 2017-02-27
              • 2012-05-08
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多