较长的睡眠(使用C ++)比较短的睡眠时间更短

时间:2011-06-08 14:40:20

标签: c++ boost sleep

我有一个任务是每隔“圆”分钟做一些事情(在xx:xx:00) 我使用像

这样的东西
const int statisticsInterval=60;
    time_t t=0;
    while (1)
    {
        if (abs(t-time(NULL)==0))   //to avoid multiple calls in the same second that is the multiple of 60
                boost::this_thread::sleep(boost::posix_time::seconds(2));//2, not 1 to make sure that 1 second passes

        t=time(NULL);
        boost::this_thread::sleep(boost::posix_time::seconds(statisticsInterval-(t%statisticsInterval)));

        //DO WORK
    }

如你所见,我使用睡眠(60秒 - 当前分钟经过的秒数)。但是一位程序员告诉我,这不准确,我应该改为 while while循环睡眠(1)。我认为他是对的非常怀疑,但我只是想检查是否有人知道如果睡眠间隔很长,精度是否较低。 我假设睡眠是以一种方式实现的,即在将来触发器被激活并且线程被置于“准备执行线程组”的某个时间,所以我认为没有理由精确地使用diff。 BTW OS是ubuntu,我不关心不到2-3秒的错误。例如,如果我睡了52秒,53.8睡眠是完全可以接受的。 附:我知道睡眠定义的最短时间,理论上我的线程可能会在2047年被激活。但我问的是现实场景。

8 个答案:

答案 0 :(得分:7)

当您进入睡眠状态(N)时,它会告诉操作系统在当前时间触发线程+ N.

它并不总是准确的原因是你不是系统中唯一的线程 在你之前可能还有另一个要求被唤醒的线程,并且可能只有一些重要的操作系统内容需要在那时完成。

无论如何,不​​应该存在任何精度问题,因为该方法与N无关。

它不是“精确”的唯一原因是它是一个糟糕的操作系统无法正确计算时间。然后,循环将无法解决这个问题。

答案 1 :(得分:7)

在某些线程API中,可以在睡眠完成之前唤醒(例如,由于在睡眠期间到达的信号)。处理此问题的正确方法是计算绝对唤醒时间,然后循环,在剩余持续时间内休眠。我会想象以一秒钟的间隔睡觉是一个黑客来近似这个,很糟糕。

但是,提升线程API的this_thread::sleep()没有记录这些早期唤醒,所以这种技术不是必需的(boost thread API为你做了循环)。

一般来说,使用较小的睡眠间隔可以显着改善唤醒延迟的情况非常少;操作系统或多或少地以相同的方式处理所有唤醒。充其量,您可以保持缓存温暖并避免页面调度,但这只会影响直接参与睡眠循环的一小部分内存。

此外,大多数操作系统在内部使用整数计数器处理时间;这意味着较大的间隔不会导致舍入错误(正如您可能会发现浮点值)。但是,如果使用浮点进行自己的计算,这可能是一个问题。如果您当前正在使用浮点间隔(例如,自1970年以来的double秒),您可能希望考虑整数单位(例如,自1970年以来的long long毫秒)。

答案 2 :(得分:4)

在许多情况下,睡眠不是很精确。这取决于操作系统的准确程度。在Windows 7中,我认为定时器分辨率约为154毫秒。此外,您通常可以告诉调度程序如何处理睡眠松弛......

这是一个很好的阅读:

Linux:http://linux.die.net/man/3/nanosleep

Windows:http://msdn.microsoft.com/en-us/library/ms686298(v=vs.85).aspx

PS:如果你想在长时间等待时获得更高的精度,请睡一段时间并根据实时时钟使用时差。即在开始睡觉时存储当前时间,然后在每个时间间隔检查您与设定等待时间的距离。

答案 3 :(得分:4)

Boost.Thread为POSIX系统实现睡眠可以使用不同的睡眠方法:

  1. 在使用Boost.Thread创建线程并且具有特定线程信息的情况下,等待互斥锁等待。
  2. 使用pthread_delay_np(如果可用)并且未使用Boost.Thread创建线程。
  3. 如果nanosleep不可用,则使用pthread_delay_np
  4. 创建一个本地互斥锁并对其进行定时等待(更糟糕的情况是,如果没有其他可用的话)。
  5. 第2,3和4个案例以5次循环实施(截至Boost 1.44)。因此,如果睡眠线程被中断(即有一些信号)超过5次 - 则可能存在潜在问题。但这不可能发生。

    在所有情况下,精确度将远高于一秒,因此进行多次睡眠并不会比做一次长时间更准确。你只能担心你的程序因长时间睡眠而被完全换掉。例如,如果机器很忙,那么内核将整个程序放在磁盘上。为了避免被换掉,你必须旋转(或做更小的睡眠并偶尔醒来)。通常,如果性能很重要,程序会在CPU上旋转并且永远不会调用睡眠,因为要避免任何阻塞调用。但如果我们谈论纳米/微秒,那就是这样。

答案 4 :(得分:2)

如果目标是在给定系统时间(xx:xx:00)之前休眠,请考虑使用boost::this_thread::sleep的重载,这需要时间,如{{1} },而不是持续时间

例如,

boost::posix_time::ptime

在C ++ 0x中,这两个重载被赋予了不同的名称:#include <iostream> #include <boost/date_time.hpp> #include <boost/thread.hpp> int main() { using namespace boost::posix_time; ptime time = boost::get_system_time(); std::cout << "time is " << time << '\n'; time_duration tod = time.time_of_day(); tod = hours(tod.hours()) + minutes(tod.minutes() + 1); time = ptime(time.date(), tod); std::cout << "sleeping to " << time << "\n"; boost::this_thread::sleep(time); std::cout << "now the time is " << boost::get_system_time() << '\n'; } std::this_thread::sleep_for();

答案 5 :(得分:2)

睡眠在调度时间量子方面起作用,除非你收到信号,否则你无法在该量子用完之前醒来。此外,睡眠不是精确或准确的。此外,时间更像是指导而非规则。它通常被理解为“至少那么多,但可能更长一段时间” 因此,60 sleep(1)永远不会比sleep(60)更准确。

由于您声明您的操作系统是Ubuntu,因此您也可以使用timerfd [1]。将过期时间设置为1分钟,然后read()。如果您获得EINTR,请再次read()。否则,你知道一分钟就到了。

这是准确的(在我不是特别令人印象深刻的Ubuntu机器上,timerfd准确地工作到微秒没问题)。作为一个优点,它也很优雅...如果您在等待时需要做其他事情,例如在套接字上侦听,您可以将timerfd插入与套接字描述符相同的epoll。您也可以在多个进程之间共享它,并同时唤醒它们。或者,或......,许多其他事情。

答案 6 :(得分:2)

一般来说,睡眠不是正确计时的方法。最好使用带回调函数的精确计时器。在Windows上,可以使用“多媒体”计时器,其在大多数硬件上的分辨率不超过1毫秒。见here。当计时器到期时,OS会在接近实时的情况下调用回调函数。见here

答案 7 :(得分:2)

答案是肯定的。但它与C ++无关。它与操作系统有关。

由于当前便携式系统更加关注低功耗,操作系统对定时器的了解越来越明显。

WindowsLinux都使用计时器松弛,以避免过于频繁地唤醒。使用超时持续时间自动计算此松弛。如果绝对需要一个非常精确的计时器,它可以通过各种方式被覆盖。

这对操作系统的作用是让它进入真正的深度睡眠状态。如果定时器一直在关闭,CPU和RAM就没有机会掉电。但是如果将定时器一起收集到批处理中,CPU可以启动,运行所有定时器操作,然后再次关闭。

因此,如果有10个程序全部休眠60秒但偏差半秒左右,那么CPU的最有效使用方法是唤醒一次,运行所有10个计时器然后再回到睡眠而不是醒来10次。