.NET Thread.Sleep()是随机不精确的

Seb*_*ski 4 .net multithreading

在我的.NET应用程序中,我必须重放一系列传感器事件.所以我创建了一个触发这些事件的线程(通常大约每1-4毫秒).我在这个线程中实现了一个循环,用于Thread.Sleep(...)让线程在事件之间休眠.

基本上它看起来像这样:

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;
  }
}
Run Code Online (Sandbox Code Playgroud)

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

我添加了一个检查(使用StopWatch,在上面的代码中不可见),关于睡眠实际花了多长时间.以下是一些结果:

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.
Run Code Online (Sandbox Code Playgroud)

为什么Thread.Sleep()表现"随机"?

笔记:

  • 在该线程运行时,Window任务管理器不显示任何cpu使用情况.
  • 行为随机变化,但持续至少几秒钟.例如,有一次当我启动线程时,它运行正常.下一次它一直运行缓慢.下一次它快速运行几秒钟,而不是减速或反过来.

更新:这里有一些伪代码,显示了Sleep行为:

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)
}
Run Code Online (Sandbox Code Playgroud)

Pet*_*hie 6

Thread.Sleep真的只是调用win32 Sleep函数,其文档详细说明了不准确性.

什么Thread.Sleep确实是放弃控制有关指定毫秒.在睡眠功能的详细信息,可能是更多或更少.OS可以返回控制的速率由系统量程确定,该量子为10-15毫秒.所以,实际上Thread.Sleep只能精确到最近的量子.但是,正如其他人所指出的那样,如果CPU负载很重,那么时间会更长.另外,如果您的线程的优先级低于所有其他线程,则可能没有时间.

在负载方面,它是关于交换机可能发生时的负载.你说的是1ms的超时(实际上大约是15,正如你所见,由于系统量子或定时器的准确性),这意味着操作系统真的只需要忙碌15-30毫秒就可以了.太忙了,无法将控制权交还给线程,直到将来的量子.那不是很多时间.如果优先级较高的东西在15-40ms内需要CPU,则优先级较高的线程会获得时间片,可能会使放弃其控制权的线程匮乏.

什么Thread.Sleep真正的意思(对于超时值大于1),它说的是OS,这个线程处于优先级低于所有其他线程在系统中至少超时值,直到线程得到控制了. Thread.Sleep它不是一种计时机制,它是放弃控制的一种手段.他们应该将"睡眠"重命名为"收益".

如果您想设计定期执行某些操作的内容,请使用计时器.如果您需要非常精确,请使用多媒体计时器.