长睡眠(在C++中)比短睡眠更不精确吗

Are longer sleeps (in C++) less precise than short ones

本文关键字:不精确 C++      更新时间:2023-10-16

我每"一轮"(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秒-当前分钟中经过的秒数)。但一位程序员告诉我,它并不精确,我应该把它改为同时循环睡眠(1)。我认为他是否正确是非常值得怀疑的,但我只想检查一下是否有人知道如果睡眠时间间隔长,是否会降低精确度。我认为睡眠是以这样一种方式实现的,即在未来的某个时刻,触发器被激活,线程被放入"准备执行线程组",所以我认为没有理由在精度上存在差异。BTW操作系统是ubuntu,我不在乎少于2-3秒的错误。例如,如果我睡了52秒,53.8的睡眠是完全可以接受的。附言:我知道睡眠定义了最短的时间,理论上我的线程可能会在2047年被激活,但我问的是现实情况。

当你睡眠(N)时,它会告诉操作系统在当前时间+N触发线程。

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

无论如何,不应该有任何精度问题,因为该方法与N.无关

它不会"精确"的唯一原因是,如果它是一个糟糕的操作系统,无法正确计算时间。再说一遍,循环并不能解决这个问题。

在一些线程API中,可能在睡眠完成之前被唤醒(例如,由于睡眠期间信号到达)。处理这个问题的正确方法是计算一个绝对的起床时间,然后循环,在剩余的时间里睡觉。我可以想象,每隔一秒钟睡一次是一种近似的方法,很糟糕。

但是,增强线程API的this_thread::sleep()没有被记录为具有这些早期唤醒,因此该技术是不必要的(增强线程API为您执行循环)。

一般来说,在极少数情况下,使用较小的睡眠间隔可以显著提高唤醒延迟;OS以或多或少相同的方式处理所有唤醒。在最好的情况下,您可能会让缓存保持温暖并避免分页,但这只会影响直接参与睡眠循环的一小部分内存。

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

在许多情况下睡眠不是很精确。这取决于操作系统的精确程度。在Windows7中,计时器的分辨率大概是15.4毫秒。此外,你通常可以告诉调度程序如何处理睡眠放松。。。

这是一个很好的阅读:

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

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

PS:如果你想在长时间等待中获得更高的精度,可以睡一段时间,并使用基于实时时钟的时间差。也就是说,当你开始睡觉时,存储当前时间,然后在每个间隔检查你离设定的等待时间有多远。

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

  1. 当线程是用Boost.thread创建的并且具有特定的线程信息时,在互斥体上等待超时
  2. 如果可用并且线程不是使用Boost.thread创建的,请使用pthread_delay_np
  3. 如果pthread_delay_np不可用,则USe nanosleep
  4. 创建一个本地互斥并对其进行定时等待(如果没有其他可用的话,情况会更糟)

案例2、3和4在5次循环中实现(截至Boost 1.44)。因此,如果睡眠线程中断(即有一些信号)超过5次,则可能存在潜在问题。但这种情况不太可能发生。

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

一般来说,睡眠不是任何事情的正确计时方法。最好使用带回调函数的精确计时器。在Windows上,可以使用"多媒体"计时器,在大多数硬件上,该计时器的分辨率不超过1毫秒。请看这里。当计时器到期时,操作系统会近乎实时地调用回调函数。请看这里。

睡眠是根据调度器的时间量来工作的(编辑:同时,大多数操作系统都支持"无提示"调度器,即不再有固定的量值,然而,原理仍然正确……有定时器合并等)。

除非你收到一个信号,否则你不可能在量子用完之前醒来。此外,sleep并不是为了精确或准确而设计的。此外,时间与其说是一条规则,不如说是一个指导原则。

虽然你可能会认为睡眠时间是"将在时间X后继续",但这根本不是发生的事情。从技术上讲,sleep的工作原理是"标记线程在大约时间X内没有准备好,然后标记它准备好,调用调度程序,然后我们看看会发生什么"。注意"准备就绪"和实际运行之间的细微差别。原则上,线程可以准备很长一段时间,并且永远不会运行
因此,60x sleep(1)可以永远sleep(60)更准确。它将使线程未就绪并再次就绪60次,并且它将调用调度器60次。由于调度程序不能在零时间内运行(线程也不能在零分钟内准备好,上下文切换也不能在0时间内完成),因此在实践中,短时间内多次睡眠必然需要比累积时间内一次睡眠花费更长的时间。

既然您声明您的操作系统是Ubuntu,那么您还可以使用timerfd[1]。将过期时间设置为1分钟,并设置为read()。如果您获得EINTR,则只需再次获得read()。否则,你知道时间到了。如果你想要精确的计时,使用计时器是正确的做法(在物理计算机上,它不可能也永远不会是100.00%的完美,但它会尽可能好,并且可以避免其他系统性错误,尤其是在重复发生事件的情况下)
POSIX timer_create函数也可以工作,它更便携,开销可能会减少半微秒左右(也许!也许不是!),但它远不如timerfd那么舒适和灵活。

你无法获得比计时器所能提供的更准确、更可靠的结果。在我并不特别令人印象深刻的Ubuntu机器上,timerfd可以精确地工作到微秒,没有问题。另外,它也很优雅。。。如果您在等待时需要做其他事情,例如监听套接字,则可以将timerfd插入与套接字描述符相同的epoll中。您也可以在多个进程之间共享它,并同时唤醒它们。或者,或者,。。。许多其他事情。

如果目标是睡眠到给定的系统时间(xx:xx:00),请考虑使用boost::this_thread::sleep的过载,该过载需要时间,如boost::posix_time::ptime中所示,而不是时间

例如

#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';
}

在C++0x中,这两个重载被赋予不同的名称:std::this_thread::sleep_for()std::this_thread::sleep_until()

答案是肯定的。然而,它与C++无关。这一切都与操作系统有关。

由于当前便携式系统更加关注低功耗使用,操作系统在计时器方面变得越来越智能。

Windows和Linux都使用定时器延迟,以避免过于频繁地醒来。此松弛是使用超时持续时间自动计算的。如果绝对需要一个真正准确的计时器,它可以通过各种方式被覆盖。

这对操作系统的作用是让它进入真正的深度睡眠状态。如果计时器一直关闭,CPU和RAM就没有机会断电。但是,如果计时器被收集到一个批次中,CPU可以通电,运行所有计时器操作,然后再次断电。

因此,如果有10个程序都睡了60秒,但偏移了半秒左右,那么CPU最有效的使用方法是唤醒一次,运行所有10个定时器,然后再回到睡眠状态,而不是唤醒10次。