Thread.Sleep(0):什么是正常行为?

时间:2010-07-15 16:29:32

标签: c# windows performance multithreading

根据我的理解,Thread.Sleep(0)强制在OS上进行上下文切换。

我想检查一下应用程序在接收一些CPU时间之前可以通过的最长时间。

所以我构建了一个在while循环(c#)中执行Thread.Sleep(0)的应用程序,并计算每次调用之间传递的时间。

当这个应用程序是在双核测试PC上运行的唯一应用程序时,最大观察时间不到1毫秒(平均0.9微秒)并且它使用所有可用的CPU(100%)。

当我沿着CPU填充虚拟应用程序(所有具有相同优先级)运行时,最大时间约为25毫秒,平均时间为20毫秒。它的行为与我期望的完全一样。时间非常稳定。

每当它获得一些CPU时间时,它会立即将控制权交给任何进行某些处理的人,这就像烫手山芋游戏(CPU使用率下降到0%)。如果没有其他应用程序运行,则控件立即返回。

鉴于此行为,我希望此应用程序对运行现实应用程序的计算机的影响最小。 (并且给我实际的“延迟”,我期望在那里运行的应用程序中看到)。但令我惊讶的是,它确实对这个特定系统的性能产生了负面影响(以可观察的方式)。

我是否遗漏了一些关于Thread.Sleep(0)的重要观点?

作为参考,这里是该应用程序的代码

private bool _running = true;
private readonly Stopwatch _timer = new Stopwatch();

private double _maxTime;
private long _count;
private double _average;
private double _current;

public Form1()
{
    InitializeComponent();
    Thread t = new Thread(Run);
    t.Start();
}

public void Run()
{
    while(_running)
    {
        _timer.Start();
        Thread.Sleep(0);
        _timer.Stop();

        _current = _timer.Elapsed.TotalMilliseconds;
        _timer.Reset();
        _count++;

        _average = _average*((_count - 1.0)/_count) + _current*(1.0/_count);
        if(_current>_maxTime)
        {
            _maxTime = _current;
        }
    }
}

为了清晰起见而编辑(申请的目的): 我目前正在运行一个软实时多线程应用程序(好吧,一组应用程序),需要每隔大约300毫秒对一些输入作出反应,但我们确实错过了一些截止日期(不到1%的时间)和我正在努力提高这个数字。

我想验证在同一台机器上由其他过程引起的当前变化是什么:我认为通过在上面的半实时机器上安装上面描述的应用程序,观察到的最大时间会告诉我由于该变量引起的变化。系统。 I.E.我有300毫秒,但是在线程获得一些CPU时间之前的最大观察时间是50毫秒,所以为了提高性能,我应该将处理时间设置为最大250毫秒(因为我可能已经晚了50毫秒)。

5 个答案:

答案 0 :(得分:33)

它没有强制上下文切换,只有Sleep(1)这样做。但是如果任何进程中的任何其他线程都准备好运行并且具有更高的优先级,那么Sleep(0)将产生处理器并让它运行。你可以通过运行一个调用Sleep(0)的无限循环来看到这一点,它会在一个内核上烧掉100%的CPU周期。我不明白为什么你不遵守这种行为。

保持系统响应的最佳方法是将线程置于低优先级。

答案 1 :(得分:6)

我在之前的项目中被这个错误所困扰。我有一个运行的线程,它会检查优先级队列中的消息,寻找新的消息。如果没有找到新消息,我希望线程进入休眠状态,直到将消息添加到队列中将其重新唤醒以再次检查。

天真地,假设Thread.Sleep(0)会导致线程进入睡眠状态直到再次唤醒,我发现一旦消息开始进入,我们的应用程序会消耗大量的CPU。

经过几天可能的原因后,我们发现了this link.的信息。快速解决方法是使用Thread.Sleep(1)。该链接包含有关差异原因的详细信息,包括底部的一个小测试应用程序,演示了两个选项之间的性能变化。

答案 2 :(得分:3)

最可能的原因是您不允许程序以有效的方式阅读说明。

当您调用Sleep(0)时,您的代码会暂停一个周期,然后安排在下一个可用的插槽中。但是,这种上下文切换并不是免费的 - 有很多寄存器可以保存/加载,当你从磁盘读取不同的指令序列时,你可能会遇到很多缓存未命中。在大多数情况下,我无法想象这会对您的应用程序产生重大影响,但如果您正在使用实时系统或类似密集型的东西,那么它可能是一种可能性。

答案 3 :(得分:3)

我的理解是Thread.Sleep(0)没有强制一个线程上下文切换,它只是告诉任务调度程序你愿意放弃剩下的时间片,如果有的话等待执行的其他线程。

你的睡眠循环(0)正在咀嚼CPU时间,这会对其他应用程序(以及笔记本电脑的电池续航时间产生负面影响!)。 Sleep(0)并不意味着“让其他所有内容先执行”,因此您的循环将与其他进程竞争执行时间。

将非零等待时间传递给Sleep()对于其他应用程序来说会略微好一点,因为它实际上会强制将此线程放在一边最少的时间。但这仍然不是你如何实现一个影响最小的后台线程。

运行CPU绑定后台线程对前台应用程序影响最小的最佳方法是将线程优先级降低到低于正常水平。这将告诉调度程序首先执行所有普通优先级线程,如果/当有任何其他时间可用时,则执行低优先级线程。这样做的副作用是,有时您的低优先级线程可能在相对较长的时间(秒)内完全没有任何执行时间,具体取决于CPU的饱和程度。

答案 4 :(得分:0)

我怀疑您注意到由于您的其他程序造成的影响也只受CPU限制。因此,这也是第三个运行您没有考虑OS调度程序的程序。

操作系统会停止未受影响的程序以允许您的程序运行,并且有一个上下文,用于交换进程,然后加载您的程序然后再次卸载它。