【问题标题】:Control loop time with usleep使用睡眠控制循环时间
【发布时间】:2018-06-28 17:48:28
【问题描述】:

我尝试使用 usleep 确保每个循环的执行时间为 10ms,但有时会超过 10ms。

我不知道如何解决这个问题,使用 usleepgettimeofday 是否合适这个案例?

请帮我找出我错过了什么。

结果:0.0127289 0.0136499 0.0151598 0.0114031 0.014801

double tvsecf(){
     struct timeval tv;
     double asec;

     gettimeofday(&tv,NULL);
     asec = tv.tv_usec;
     asec /= 1e6;
     asec += tv.tv_sec;

     return asec;
}
int main(){
    double t1 ,t2;
    t1 = tvsecf();
    for(;;){
        t2= tvsecf();
        if(t2-t1 >= 0.01){
            if(t2-t1 >= 0.011)
                cout << t2-t1 <<endl;
            t1 = tvsecf();
        }
        usleep(100);
    }
}

【问题讨论】:

  • 首先,您必须确定您使用的是哪种语言……
  • 通常,只要您要求,睡眠只能保证睡至少 - 他们可能会睡得更久。
  • 我认为nanosleep 是首选睡眠。
  • 这听起来像XY problem。您实际上想要达到什么目的?
  • 如果您确实需要每 10 毫秒执行一次操作且不会失败(并且不要只是认为您会这样做),那么您将需要在实时操作系统上运行您的程序

标签: c++ linux time gettimeofday


【解决方案1】:

为了防止循环开销(通常是未知的)不断累积错误,您可以睡眠直到一个时间,而不是for 一个时间持续时间。使用 C++ 的 &lt;chrono&gt;&lt;thread&gt; 库,这非常简单:

#include <chrono>
#include <iostream>
#include <thread>

int
main()
{
    using namespace std;
    using namespace std::chrono;
    auto t0 = steady_clock::now() + 10ms;
    for (;;)
    {
        this_thread::sleep_until(t0);
        t0 += 10ms;
    }
}

人们可以通过更多地调用steady_clock::now() 来修饰这一点,以确定迭代之间的时间,也许更重要的是,平均迭代时间:

#include <chrono>
#include <iostream>
#include <thread>

int
main()
{
    using namespace std;
    using namespace std::chrono;
    using dsec = duration<double>;
    auto t0 = steady_clock::now() + 10ms;
    auto t1 = steady_clock::now();
    auto t2 = t1;
    constexpr auto N = 1000;
    dsec avg{0};
    for (auto i = 0; i < N; ++i)
    {
        this_thread::sleep_until(t0);
        t0 += 10ms;
        t2 = steady_clock::now();
        dsec delta = t2-t1;
        std::cout << delta.count() << "s\n";
        avg += delta;
        t1 = t2;
    }
    avg /= N;
    cout << "avg = " << avg.count() << "s\n";
}

上面我通过在循环中做更多的事情来增加循环开销。然而,循环仍然会每 10 毫秒唤醒一次大约。有时操作系统会延迟唤醒线程,但下一次循环会自动调整自己休眠更短的时间。因此平均迭代率自校正到 10ms。

在我的机器上这只是输出:

...
0.0102046s
0.0128338s
0.00700504s
0.0116826s
0.00785826s
0.0107023s
0.00912614s
0.0104725s
0.010489s
0.0112545s
0.00906409s
avg = 0.0100014s

【讨论】:

  • 是的!平均迭代时间对我的应用程序来说是最重要的。
【解决方案2】:

没有办法保证 10 毫秒的循环时间。 所有睡眠功能都至少睡眠所需的时间。 对于便携式解决方案,请使用 std::this_thread::sleep_for

#include <iostream>
#include <chrono>
#include <thread>

int main()
{
    for (;;) {
        auto start = std::chrono::high_resolution_clock::now();
        std::this_thread::sleep_for(std::chrono::milliseconds{10});
        auto end = std::chrono::high_resolution_clock::now();
        std::chrono::duration<double, std::milli> elapsed = end-start;
        std::cout << "Waited " << elapsed.count() << " ms\n";
    }
}

根据您要执行的操作,查看 Howard Hinnants 日期库。

【讨论】:

    【解决方案3】:

    来自usleep man page

    任何系统活动或处理调用所花费的时间或系统计时器的粒度都可能会稍微延长睡眠时间。

    如果您需要高分辨率:在 Unix(或 Linux)上使用 C 查看 this answer,它解释了如何使用 clock_gettime 来使用高分辨率计时器。

    编辑:正如 Tobias nanosleep 所述,可能是更好的选择:

    Compared to sleep(3) and usleep(3), nanosleep() has the following
    advantages: it provides a higher resolution for specifying the sleep
    interval; POSIX.1 explicitly specifies that it does not interact with
    signals; and it makes the task of resuming a sleep that has been
    interrupted by a signal handler easier.
    

    【讨论】:

    • 也来自 usleep 手册页。 POSIX.1-2001 声明此函数已过时,请改用 nanosleep(2)。 POSIX.1-2008 删除了 usleep() 的规范
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-13
    • 1970-01-01
    • 2011-07-19
    • 1970-01-01
    相关资源
    最近更新 更多