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

Ben*_*ttr 24 c# windows performance multithreading

据我所知,Thread.Sleep(0)强制操作系统上下文切换.

我想检查在接收一些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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

Han*_*ant 33

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

保持系统响应的最佳方法是为您的线程提供低优先级.


dth*_*rpe 6

我的理解是 Thread.Sleep(0) 不会强制线程上下文切换,它只是向任务调度程序发出信号,如果有其他线程等待执行,您愿意放弃剩余的时间片。

您围绕 Sleep(0) 的循环正在消耗 CPU 时间,这将对其他应用程序(以及笔记本电脑的电池寿命!)产生负面影响。Sleep(0) 并不意味着“让其他一切先执行”,因此您的循环将与其他进程竞争执行时间。

将非零等待时间传递给 Sleep() 对其他应用程序来说会稍微好一些,因为它实际上会强制将此线程搁置最少的时间。但这仍然不是您实现最小影响后台线程的方式。

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

  • 是的,您将剩余的时间片交给他们,但是您这样做如此频繁/如此迅速,以至于您可能在任务切换上比在执行应用程序代码上消耗了更多时间。任务切换不是一项简单的操作——它需要数百或数千个 CPU 时钟周期来存储一个线程的所有寄存器和执行上下文并加载另一个线程的所有上下文。您的线程不是空闲的,它在一个紧密的循环中旋转并迫使任务调度程序以比平时更快的速度切换上下文。 (2认同)

Eri*_*son 6

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

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

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

  • 听起来你真的想要使用`WaitHandle` ......? (7认同)