如果我正在使用持续时间进行timed_wait,但系统时间发生变化会怎样?

8

当使用带有时间段的 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:好的,但是为了记录,你的回答当时与我的实验结果相符。 - indiv
4个回答

8
截至撰写日期(2013年11月),如果您在等待boost条件变量时,挂钟时间发生更改,则会得到错误的结果。
如果您不必使用boost,则可以使用所谓的“单调时钟”。由于单调时钟不受挂钟时间更改的影响,因此它不容易遇到您所描述的问题。您可以安全地使用pthread 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


2
我认为这是一个竞态条件,尽管非常罕见。condition_variable::timed_wait()的实现使用duration将值转换为system_time,使用get_system_time()和wait_duration计算出等待结束时间。如果在调用get_system_time()获取系统时间和重新将计算出的等待结束时间转换为基于tick的计数器进行下层OS调用之间系统时间发生了改变,那么你的等待时间就会出错。
为了测试这个想法,在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 );
}

第一个线程虽然应该每100毫秒输出"Ping!",但很快就卡住了。

除非我漏掉了什么,否则似乎Boost没有提供任何API来避免这个内部转换到系统时间的问题,使应用程序容易受到时钟外部变化的影响。


1

修复已在Boost 1.60版本中发布:http://www.boost.org/users/history/version_1_60_0.html - SimonAlfie
@SimonAlfie,你能告诉我这个修复程序是否适用于Windows操作系统吗?如果是,它会默认工作还是需要先调用一些API?如果不行,我的选择是什么? - Swamy
1
@swamy 不确定这个问题是否影响所有平台。只有在Linux上报告过(我怀疑它会影响所有平台)。为什么不尝试更新你的boost版本,看看是否可以解决呢?无需先调用任何API。 - SimonAlfie
@SimonAlfie 感谢您的回复。我已经在Windows机器上检查了v1.62。我正在使用wait_until API和steady_clock,当时间向前/向后改变时,等待的时间量是相同的。例如:如果我等到12:00:30并将时钟向前移动5秒,则等待将在12:00:35到期。向后时间更改也适用于相同的规则。 - Swamy

1

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

我们有一个进程,使用POSIX计时器以20 Hz的准确时间进行定时。当该计时器被激活并设置为早期日期/时间时,条件变量会阻塞。当我将时间更改回原始值时,条件变量继续执行。

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

如果有可能将条件变量的模式设置为单调的,那将非常有帮助,但目前尚不可能实现此功能。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接