我有一个任务是每隔“圆”分钟做一些事情(在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年被激活。但我问的是现实场景。
答案 0 :(得分:7)
当您进入睡眠状态(N)时,它会告诉操作系统在当前时间触发线程+ N.
它并不总是准确的原因是你不是系统中唯一的线程 在你之前可能还有另一个要求被唤醒的线程,并且可能只有一些重要的操作系统内容需要在那时完成。
无论如何,不应该存在任何精度问题,因为该方法与N无关。
它不是“精确”的唯一原因是它是一个糟糕的操作系统无法正确计算时间。然后,循环将无法解决这个问题。
答案 1 :(得分:7)
在某些线程API中,可以在睡眠完成之前唤醒(例如,由于在睡眠期间到达的信号)。处理此问题的正确方法是计算绝对唤醒时间,然后循环,在剩余持续时间内休眠。我会想象以一秒钟的间隔睡觉是一个黑客来近似这个,很糟糕。
但是,提升线程API的this_thread::sleep()
没有记录这些早期唤醒,所以这种技术不是必需的(boost thread API为你做了循环)。
一般来说,使用较小的睡眠间隔可以显着改善唤醒延迟的情况非常少;操作系统或多或少地以相同的方式处理所有唤醒。充其量,您可以保持缓存温暖并避免页面调度,但这只会影响直接参与睡眠循环的一小部分内存。
此外,大多数操作系统在内部使用整数计数器处理时间;这意味着较大的间隔不会导致舍入错误(正如您可能会发现浮点值)。但是,如果你使用浮点进行自己的计算,这可能是一个问题。如果您当前正在使用浮点间隔(例如,自1970年以来的double
秒),您可能希望考虑整数单位(例如,自1970年以来的long long
毫秒)。
答案 2 :(得分:4)
这是一个很好的阅读:
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系统实现睡眠可以使用不同的睡眠方法:
pthread_delay_np
(如果可用)并且未使用Boost.Thread创建线程。nanosleep
不可用,则使用pthread_delay_np
。第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 ++无关。它与操作系统有关。
由于当前便携式系统更加关注低功耗,操作系统对定时器的了解越来越明显。
Windows和Linux都使用计时器松弛,以避免过于频繁地唤醒。使用超时持续时间自动计算此松弛。如果绝对需要一个非常精确的计时器,它可以通过各种方式被覆盖。
这对操作系统的作用是让它进入真正的深度睡眠状态。如果定时器一直在关闭,CPU和RAM就没有机会掉电。但是如果将定时器一起收集到批处理中,CPU可以启动,运行所有定时器操作,然后再次关闭。
因此,如果有10个程序全部休眠60秒但偏差半秒左右,那么CPU的最有效使用方法是唤醒一次,运行所有10个计时器然后再回到睡眠而不是醒来10次。