如果在我执行 timed_wait 持续时间时系统时间发生变化怎么办?

Posted

技术标签:

【中文标题】如果在我执行 timed_wait 持续时间时系统时间发生变化怎么办?【英文标题】:What if the system time changes while I'm doing timed_wait with a duration? 【发布时间】:2011-05-21 19:59:52 【问题描述】:

当在带有持续时间的boost::condition_variable 上使用timed_wait 时,即使用户(或 ntp)更改系统时间,等待条件是否会在持续时间之后超时?

例如,

boost::posix_time::time_duration wait_duration(0, 0, 1, 0);  // 1 sec
// ** System time jumps back 15 minutes here. **
if( !signal.timed_wait(lock, wait_duration) )

    // Does this condition happen 1 second later, or about 15 minutes later?

【问题讨论】:

@Roddy:好的,但为了记录,你的答案与我当时的实验结果相符。 【参考方案1】:

问题已在 1.61 开发分支修复:

https://svn.boost.org/trac/boost/ticket/6377

【讨论】:

修复在 boost 1.60 版本中发布:boost.org/users/history/version_1_60_0.html @SimonAlfie 你能告诉我这个修复是否适用于 Windows 操作系统吗?如果是,这将默认工作还是我需要先调用一些 API?如果否,我的选择是什么? @swamy 不确定问题是否影响所有平台。仅在 Linux 上被报告(我怀疑它会影响所有人)。为什么不尝试更新你的 boost 版本,看看它是否能修复它?无需先调用任何 API。 @SimonAlfie 感谢您的回复。我能够在 Windows 机器上签入 v1.62。我将wait_until API 与steady_clock 一起使用,当时间向前/向后更改时,等待的时间是相同的。例如:如果我等到12:00:30 并将时钟向前移动5sec,则等待将在12:00:35 到期。同样适用于向后的时间变化。【参考方案2】:

截至本文撰写之日(2013 年 11 月),如果挂钟时间在您等待升压条件变量时发生变化,您只会得到糟糕的结果。

如果您不必必须使用 boost,则可以使用所谓的“单调时钟”。由于单调时钟不受挂钟时间变化的影响,因此它不易受到您描述的问题的影响。您可以使用 pthreads API 安全地等待 5 秒钟,如下所示:

pthread_condattr_t attr;
pthread_cond_t cond;
struct timespec ts;

pthread_condattr_init(&attr);
pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
pthread_cond_init(&cond, &attr);
pthread_condattr_destroy(&attr);
clock_gettime(CLOCK_MONOTONIC, &ts);
ts.tv_sec += 5;
pthreead_cond_timedwait(&cond, &mutex, &ts);

您可以检查 boost::condition_variable 的实现。也许他们有一天会解决这个问题。实现在这里:http://svn.boost.org/svn/boost/trunk/boost/thread/pthread/condition_variable.hpp

【讨论】:

【参考方案3】:

我相信这是一种竞争条件,尽管非常罕见。具有持续时间的 condition_variable::timed_wait() 的实现只是使用 get_system_time()+wait_duration 将值转换为 system_time。如果系统时间在 get_system_time() 被调用之间发生变化,并且计算出的等待结束时间被重新转换为底层操作系统调用的基于滴答的计数器,那么您的等待时间将是错误的。

为了测试这个想法,我在 Windows 上编写了一个简单的程序,其中一个线程每 100 毫秒生成一些输出,如下所示:

for (;;)

    boost::this_thread::sleep( boost::get_system_time() +
        boost::posix_time::milliseconds( 100 ) );
    std::cout << "Ping!" << std::endl;

另一个线程每 100 毫秒将系统时间设置回过去一分钟(该线程使用操作系统级别的“Sleep()”调用来避免转换为系统时间):

for ( ;; )

    Sleep( 100 );
    SYSTEMTIME sysTime;
    GetSystemTime( &sysTime );
    FILETIME fileTime;
    SystemTimeToFileTime( &sysTime, /*out*/&fileTime );
    ULARGE_INTEGER fileTime64 = (ULARGE_INTEGER(fileTime.dwHighDateTime) << 32) |
        fileTime.dwLowDateTime;
    fileTime64 -= 10000000 * 60;   // one minute in the past
    fileTime.dwHighDateTime = (fileTime64>>32) & 0xFFFFFFFF;
    fileTime.dwLowDateTime = fileTime64 & 0xFFFFFFFF;
    FileTimeToSystemTime( &fileTime, /*out*/&sysTime );
    SetSystemTime( &sysTime );

第一个线程,虽然应该输出“Ping!”每 100 毫秒,锁定相当快。

除非我遗漏了什么,否则 Boost 似乎没有提供任何 API 来避免内部转换为系统时间的问题,从而使应用容易受到外部时钟更改的影响。

【讨论】:

【参考方案4】:

如果您的进程也使用信号,我确实看到了一些问题。我还使用带有持续时间的 Boost 条件变量。

我们有一个流程,它使用 POSIX 计时器来获得 20 Hz 的准确计时。当此计时器被激活并且时间设置为较早的日期/时间时,条件变量会阻塞。当我将时间改回原始值时,条件变量会继续。

我从 Boost 复制了实现并将时钟模式设置为 CLOCK_MONOTONIC。现在即使时间改变,条件变量也能正常工作。

如果有可能将条件变量的模式设置为单调,那将会很有帮助,但目前这是不可能的。

【讨论】:

以上是关于如果在我执行 timed_wait 持续时间时系统时间发生变化怎么办?的主要内容,如果未能解决你的问题,请参考以下文章

Jstack线程状态BLOCKED/TIMED_WAITING/WAITING解释

如何持续监控某些网站并定期执行操作

转:java线程状态说明,Jstack线程状态BLOCKED/TIMED_WAITING/WAITING解释

提升进程间匿名条件 timed_wait 不可编译

C++的本地持续集成系统?

鸿蒙操作系统应用笔记211211A