使用sleep_for每毫秒运行一次循环

Run loop every millisecond with sleep_for

本文关键字:循环 一次 运行 sleep for 使用      更新时间:2023-10-16

我想在线程内运行一个循环,每毫秒计算一些数据。但是我的睡眠功能有问题。它睡得太久了。

我在visual studio中创建了一个基本的控制台应用程序:
#include <windows.h>
#include <iostream>
#include <chrono>
#include <thread>
using namespace std;
typedef std::chrono::high_resolution_clock Clock;
int _tmain(int argc, _TCHAR* argv[])
{
    int iIdx = 0;
    bool bRun = true;
    auto aTimeStart = Clock::now();
    while (bRun){
        iIdx++;
        if (iIdx >= 500) bRun = false;
        //Sleep(1);
        this_thread::sleep_for(chrono::microseconds(10));
    }
    printf("Duration: %i msn", chrono::duration_cast<std::chrono::milliseconds>(Clock::now() - aTimeStart).count());
    cin.get();
    return 0;
}

打印结果:持续时间:5000 ms当我使用Sleep(1);

时,输出相同的结果

我希望持续时间是500 ms,而不是5000 ms。我哪里做错了?

更新:


我使用的是Visual Studio 2013。现在我已经安装了Visual Studio 2015,它的精细打印出来:持续时间:500毫秒(有时是527毫秒)。

然而,这个sleep_for仍然不是很准确,所以我将寻找其他解决方案。

流行操作系统使用的典型时间片远长于1ms(比如20ms左右);sleep设置了您希望线程挂起的最小时间,而不是最大值。一旦你的线程变得可运行,下一次调度它的时间就由操作系统决定了。

如果你需要这种级别的精度,你要么需要一个实时的操作系统,或者在你的线程上设置一个非常高的优先级(这样它就可以抢占几乎任何其他东西),或者在内核中编写代码,或者使用一个繁忙的等待。

但是你真的需要每毫秒计算一次吗?这种时序要求通常来自硬件。如果稍后再进行计算,会出现什么问题?

在Windows上,尝试timeBeginPeriod: https://msdn.microsoft.com/en-us/library/windows/desktop/dd757624(v=vs.85).aspx

我在这里做错了什么?

尝试使用sleep进行精确计时。

sleep(n)不会在n时间内暂停线程,然后立即继续。

sleep(n)将线程的控制权交还给调度器,并指出在至少 n时间过去之前,您不希望控制权交还给调度器。

现在,调度器已经将线程处理时间划分为时间片,这些时间片通常在25毫秒左右。这是你可以期望你的睡眠运行的最小

sleep对于这项工作来说是一个错误的工具。永远不要使用它来精确调度。

这个线程相当老了,但是也许有人仍然可以使用这个代码。

它是为c++ 11编写的,我在Ubuntu 15.04上测试了它。

class MillisecondPerLoop
  {
  public:
    void do_loop(uint32_t loops)
      {
      int32_t time_to_wait = 0;
      next_clock = ((get_current_clock_ns() / one_ms_in_ns) * one_ms_in_ns);
      for (uint32_t loop = 0; loop < loops; ++loop)
        {
        on_tick();
        // Assume on_tick takes less than 1 ms to run
        // calculate the next tick time and time to wait from now until that time
        time_to_wait = calc_time_to_wait();
        // check if we're already past the 1ms time interval
        if (time_to_wait > 0)
          {
          // wait that many ns
          std::this_thread::sleep_for(std::chrono::nanoseconds(time_to_wait));
          }
        ++m_tick;
      }
    }
 private:
   void on_tick()
     {
     // TEST only: simulate the work done in every tick
     // by waiting a random amount of time
      std::this_thread::sleep_for(std::chrono::microseconds(distribution(generator)));
     }
   uint32_t get_current_clock_ns()
     {
     return std::chrono::duration_cast<std::chrono::nanoseconds>(
     std::chrono::system_clock::now().time_since_epoch()).count();
     }
   int32_t calc_time_to_wait()
     {
     next_clock += one_ms_in_ns;
     return next_clock - get_current_clock_ns();
     }
   static constexpr uint32_t one_ms_in_ns = 1000000L;
   uint32_t m_tick;
   uint32_t next_clock;
 };

典型的运行显示了一个非常精确的1ms循环,误差为1- 3微秒。如果你的电脑有更快的CPU,可能会比这更准确。

典型输出:

One Second Loops:
        Avg (ns)       ms   err(ms)
   [ 0]   999703   0.9997   0.0003
   [ 1]   999888   0.9999   0.0001
   [ 2]   999781   0.9998   0.0002
   [ 3]   999896   0.9999   0.0001
   [ 4]   999772   0.9998   0.0002
   [ 5]   999759   0.9998   0.0002
   [ 6]   999879   0.9999   0.0001
   [ 7]   999915   0.9999   0.0001
   [ 8]  1000043   1.0000  -0.0000
   [ 9]   999675   0.9997   0.0003
   [10]  1000120   1.0001  -0.0001
   [11]   999606   0.9996   0.0004
   [12]   999714   0.9997   0.0003
   [13]  1000171   1.0002  -0.0002
   [14]   999670   0.9997   0.0003
   [15]   999832   0.9998   0.0002
   [16]   999812   0.9998   0.0002
   [17]   999868   0.9999   0.0001
   [18]  1000096   1.0001  -0.0001
   [19]   999665   0.9997   0.0003
Expected total time: 20.0000ms
Actual total time  : 19.9969ms

我有一个更详细的写在这里:https://arrizza.org/wiki/index.php/One_Millisecond_Loop