C#中Thread.Sleep(1)的影响是什么?

30
在 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使用率?


到目前为止,所有的答案都表明这种影响是微不足道的,但您能否对您试图通过此达到的目的进行一些澄清? - JMD
我只是想看看我的GUI应用程序是否会因为短暂的1毫秒延迟而在这个线程中花费所有时间。 - Justin Tanner
10个回答

46

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

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

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


你能举个在C#中设置timeBeginPeriod的例子吗? - Justin Tanner
3
根据MSDN文档,timeBeginPeriod应与timeEndPeriod配对使用。同时请注意,“此函数会影响全局Windows设置”,可能会影响系统性能。 - Bob Nadler

28

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


1
这会不必要地占用大量的CPU时间。 - Joel Coehoorn
@Joel 它为什么会比while(true){i++;}占用更多时间,或者从CPU使用方面来说本质上是相同的吗? - DevinB
8
在现代 CPU 上,相比于执行 i++,1 毫秒的休眠时间就显得异常漫长。 - Serguei
相对于一个有10个功能的计算器总可用CPU时间,是的,它会占用很多CPU时间。相对于过去十年中制造的任何机器的可用CPU时间,它将使用大约零CPU时间。 - Mark

21

Thread.Sleep(1)不会独占CPU。

当线程休眠时,大致发生以下情况:

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

最后,我不确定你在做什么,但看起来你试图扮演调度程序的角色,即通过休眠来提供CPU时间去做其他事情...

在极少数情况下,这可能没问题,但大多数情况下,你应该让调度程序完成它的工作,它可能比你更了解情况可以比你做得更好


6

应避免小睡眠时间,因为当线程放弃其时间片时,重新发出信号会使其获得优先级提升,这可能导致上下文切换增加。在多线程/服务器应用中,这可能会导致竞争CPU时间的线程之间产生抖动效果。相反,应依赖异步函数和同步对象,如关键节或互斥量/信号量。


4

这是一个经常出现在我的搜索结果中的旧帖子,但是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。因此,在1秒的时间跨度内,使用1毫秒的睡眠,我得到了946次唤醒。这非常接近1毫秒。
第二部分询问完成1000个1毫秒的睡眠事件需要多长时间。我得到了1034毫秒。同样,接近1毫秒。
这是在1.8ghz core2duo + Win7上使用.Net 4.0进行的。
编辑:请记住,sleep(x)并不意味着在此时间唤醒,它意味着在此时间之后唤醒我。这不能保证。但是,您可以提高线程的优先级,Windows应该在低优先级线程之前安排您的线程。

1
关于Win 7调度程序的评论。我运行了Bengie的示例“namespace ConsoleApplication2”帖子,并报告了65和15600,表明大约有15毫秒的步骤。这是一个双3.33Mhx运行.NET 3.5,Win 7的系统。奇怪的是,昨天我有一些代码日志,表现出接近1ms的sleep(1),就像他的帖子所说的那样。我的结论是,我的机器上有一些设置或配置已经改变,但我无法确定。在调试或发布模式下,使用或不使用调试器都是相同的答案。我没有使用begin/end TimePeriod。主机负载非常小。 - crokusek

4

正如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


2
不会占用所有可用的CPU,因为当另一个线程有工作要做时,睡眠线程将被操作系统调度程序切换出去。

2
不会的。你几乎看不到它。在不到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使用率。


我怀疑这种情况甚至不会经常发生:sleep的参数是最短时间,而上下文切换需要太长时间才能让很多人进入。 - Joel Coehoorn
在现代多核系统上,1毫秒是非常长的时间。我想知道这个循环实际上会如何运行? - krosenvold

0

同时也可以看一下这个:MSDN论坛

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

0

一个线程最多同时占用一个(逻辑)CPU。而1毫秒的休眠不会占用CPU。不要担心。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接