.NET Thread.sleep()方法是随机的IM precise [英] .NET Thread.Sleep() is randomly imprecise

查看:181
本文介绍了.NET Thread.sleep()方法是随机的IM precise的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的.net应用程序我有重播一系列的传感器事件。所以,我创建了一个线程触发这些事件(通常大约每1 - 4毫秒)。我在这个线程实现一个循环,并使用 Thread.sleep代码(...)把该线程的事件之间睡觉。

基本上,它看起来是这样的:

 无效RunThread(){
  。VAR ITER = GetAllEvents()的GetEnumerator();
  如果(!iter.MoveNext()){
    返回;
  }

  日期时间lastEventTime = iter.Current.Timestamp;
  FireEvent(iter.Current);

  而(iter.MoveNext()){
    MyEvent nextEvent = iter.Current;

    INT超时=(INT)(nextEvent.Timestamp  -  lastEventTime).TotalMilliseconds;
    Thread.sleep代码(超时);

    FireEvent(nextEvent);
    lastEventTime = nextEvent.Timestamp;
  }
}
 

现在,我的问题是,的Thread.Sleep()有时尊重指定的超时,有时没有。

我添加了一个检查(使用秒表,在code以上不可见)多长时间的睡眠居然拿了。下面是一些结果:

1毫秒

 预计超时却得到了15毫秒。
预计2毫秒超时,但拿到了13毫秒。
预计3毫秒的超时时间,但得到15毫秒。
预计2毫秒超时,但拿到了13毫秒。
预计1毫秒超时,但拿到了13毫秒。
预计1毫秒超时,但得到15毫秒。
预计2毫秒超时,但拿到了13毫秒。
预计2毫秒超时却得到了40毫秒。
 

为什么的Thread.Sleep()的行为是随机?

注:

  • 在窗口任务管理器不显示任何CPU使用率这个线程运行时。
  • 行为随机变化而持续至少几秒钟。例如,当我启动线程一次,它运行正常。在下次运行时慢的全部时间。下一次它运行快了几秒钟,比减慢或其他方式。

更新:下面是一些伪code,显示了如何睡眠表现:

 布尔ChooseRespectTimeout(){
  如果(this.notYetChosen){
    this.respectTimeout = ChooseRandomly()
    this.notYetChosen = FALSE
    经过一段随机时间重置this.notYetChosen
  }
  返回this.respectTimeout
}

无效睡眠(INT超时){
  如果(ChooseRespectTimeout())
    Thread.sleep代码(超时)
  其他
    Thread.sleep代码(超时* 10)
}
 

解决方案

Thread.sleep代码真的只是调用的Win32的休眠功能,它的文档详细介绍了误差。

什么 Thread.sleep代码确实是放弃控制的有关的指定毫秒。该睡眠功能细节,可以是更多或更少。在该OS可以回馈控制的速率是由系统量子,其为10-15毫秒确定。因此,在现实中 Thread.sleep代码只能精确到量子。但是,正如其他人所指出的那样,如果CPU的负载非常大的,时间会更长。另外,如果您的线程的优先级低于所有其他线程,它可能无法得到的时间。

在载荷方面,它是关于负载时可能出现的开关。你在谈论一个1ms的超时(这实际上是15,因为你已经看到,由于该系统的量子或定时精度),这意味着操作系统真的只需要忙15-30毫秒才可以忙不过来回馈控制线程,直到未来的量子。这不是一个整体大量的时间。如果有什么更高的优先级都需要CPU在15-40ms,高优先级的线程获得的时间片段,可能饿死放弃其控制线程。

什么 Thread.sleep代码的真正含义(用于超时值大于1)是它告诉这个线程是低优先级比的每线程操作系统的在系统中的至少的超时值,直到线程得到控制了。 Thread.sleep代码不是一个计时机制,它放弃控制权的一种手段。他们真的应该重新命名睡眠,以收益。

如果你想设计的东西,定期做一些事情,用一个定时器。如果你需要真正的precise,使用多媒体计时器。

In my .NET application I have to replay a series of sensor events. So I created a thread that fires these events (usually about every 1 - 4 millisecond). I implemented a loop in this thread and used Thread.Sleep(...) to put the thread to sleep between the events.

Basically it looks like this:

void RunThread() {
  var iter = GetAllEvents().GetEnumerator();
  if (!iter.MoveNext()) {
    return;
  }

  DateTime lastEventTime = iter.Current.Timestamp;
  FireEvent(iter.Current);

  while (iter.MoveNext()) {
    MyEvent nextEvent = iter.Current;

    int timeout = (int)(nextEvent.Timestamp - lastEventTime).TotalMilliseconds; 
    Thread.Sleep(timeout);

    FireEvent(nextEvent);
    lastEventTime = nextEvent.Timestamp;
  }
}

Now, my problem is that Thread.Sleep() sometimes respects the specified timeout and sometimes it doesn't.

I added a check (using StopWatch, not visible in the code above) on how long the sleep actually took. Here are some results:

Expected timeout of 1 ms but got 15 ms.
Expected timeout of 2 ms but got 13 ms.
Expected timeout of 3 ms but got 15 ms.
Expected timeout of 2 ms but got 13 ms.
Expected timeout of 1 ms but got 13 ms.
Expected timeout of 1 ms but got 15 ms.
Expected timeout of 2 ms but got 13 ms.
Expected timeout of 2 ms but got 40 ms.

Why does Thread.Sleep() behave that "randomly"?

Notes:

  • The Window task manager doesn't show any cpu usage while this thread runs.
  • The behavior changes randomly but persists for at least a couple of seconds. For example, one time when I start the thread, it does run fine. The next time it runs slow the whole time. The next time it runs fast for a couple of seconds, than slows down or the other way around.

Update: Here's some pseudo-code that shows how Sleep behaves:

bool ChooseRespectTimeout() {
  if (this.notYetChosen) {
    this.respectTimeout = ChooseRandomly()
    this.notYetChosen = false
    reset this.notYetChosen after random time period
  }
  return this.respectTimeout
}

void Sleep(int timeout) {
  if (ChooseRespectTimeout())
    Thread.Sleep(timeout)
  else
    Thread.Sleep(timeout * 10)
}

解决方案

Thread.Sleep really just calls win32 Sleep function, whose documentation details the inaccuracy.

What Thread.Sleep really does is give up control for about the specified milliseconds. The Sleep function details that could be more or less. The rate at which the OS can give back control is determined by the system quantum, which is 10-15 milliseconds. So, in reality Thread.Sleep can only be accurate to the nearest quantum. But, as others have pointed out, if the CPU is under heavy load that, time will be longer. Plus, if your thread has a lower priority than all other threads, it may not get time.

In terms of load, it's about load when the switch might occur. You're talking about a 1ms timeout (which is really about 15, as you've witnessed, due to the system quantum or timer accuracy) which means that the OS really only needs to be busy for 15-30 ms before it can be too busy to give back control to a thread until a future quanta. That's not a whole lot of time. If something of higher priority needs CPU in that 15-40ms, the higher-priority threads gets the time-slice, potentially starving the thread that gave up its control.

What Thread.Sleep really means (for timeout values larger than 1) is that it's telling the OS that this thread is lower in priority than every other thread in the system for at least the timeout value, until the thread gets control back. Thread.Sleep is not a timing mechanism, it's a means of relinquishing control. They should really rename "Sleep" to "Yield".

If you want to design something that periodically does something, use a timer. If you need to be really precise, use a multimedia timer.

这篇关于.NET Thread.sleep()方法是随机的IM precise的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆