在等待std :: condition_variable时如何处理系统时钟更改?

时间:2018-06-23 21:42:03

标签: c++ c++11 time condition-variable system-clock

我正在尝试在C ++ 11中实现一些跨平台代码。此代码的一部分使用std::condition_variable实现信号量对象。当我需要对信号量进行定时等待时,可以使用wait_until或wait_for。

我遇到的问题是,在基于POSIX的系统上,condition_variable的标准实现似乎依赖于on the system clock, rather than the monotonic clock(另请参见:this issue against the POSIX spec

这意味着,如果系统时钟更改为过去的某个时间,我的条件变量将比我期望的更长。例如,如果我希望我的condition_variable在1秒后超时,如果有人在等待期间将时钟调整回10分钟,则condition_variable会阻塞10分钟+ 1秒。我已经确认这是Ubuntu 14.04 LTS系统上的行为。

我需要依靠此超时至少在某种程度上是准确的(即,在一定的误差范围内可能不准确,但是如果系统时钟改变,仍然需要执行)。似乎我需要做的是编写自己的condition_variable版本,该版本使用POSIX函数并使用单调时钟实现相同的接口。

这听起来像很多工作-有点混乱。还有其他解决此问题的方法吗?

4 个答案:

答案 0 :(得分:3)

在考虑了可能解决此问题的方法之后,似乎最有意义的方法是禁止使用std :: condition_variable(或至少要说明一点,即始终使用系统时钟)。 。然后,我必须以尊重时钟选择的方式从根本上自己重新实现标准库的condition_variable。

由于我必须支持多个平台(Bionic,POSIX,Windows,最后是MacOS),所以我将维护该代码的多个版本。

虽然这很讨厌,但看起来替代品甚至更讨厌。

答案 1 :(得分:1)

集中注册活动条件变量。

尽力检测时钟错误,即使它是当前时钟上的线程自旋锁(ick)或其他手段。

检测到时钟错误时,请戳条件变量。

现在将条件变量包装在一个薄包装器中,该包装还支持检测时钟滑移。它调用wait_until,但用检测时钟滑移的谓词替换谓词,并且在发生这种情况时就不再等待。

当您的实现失败时,您必须做自己必须做的事情。

答案 2 :(得分:1)

我遇到了同样的问题。我的一位同事给了我一个提示,改而使用<pthread.h>中的某些C函数,对我来说效果很好。

例如,我有:

std::mutex m_dataAccessMutex;
std::condition_variable m_dataAvailableCondition;

及其标准用法:

std::unique_lock<std::mutex> _(m_dataAccessMutex);
// ...
m_dataAvailableCondition.notify_all();
// ...
m_dataAvailableCondition.wait_for(...); 

可以使用pthread_mutex_tpthread_cond_t替换以上内容。好处是您可以将时钟指定为单调。简要用法示例:

#include <pthread.h>

// Declare the necessary variables
pthread_mutex_t m_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_condattr_t m_attr;
pthread_cond_t m_cond;

// Set clock to monotonic
pthread_condattr_init(&m_attr);
pthread_condattr_setclock(&m_attr, CLOCK_MONOTONIC);
pthread_cond_init(&m_cond, &m_attr); 

// Wait on data
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
ts.tv_sec += timout_in_seconds; 
pthread_mutex_lock(&m_mutex);
int rc = pthread_cond_timedwait(&m_cond, &m_mutex, &ts);
if (rc != ETIMEDOUT)
    ; // do things with data
else 
    ; // error: timeout
// ...
pthread_mutex_unlock(&m_mutex); // have to do it manually to unlock
// ...

// Notify the data is ready
pthread_cond_broadcast(&m_cond);

答案 3 :(得分:0)

这可能不是最好的解决方案,也不是一个很好的解决方案,但是您说的是“变通”而不是“很多工作”,所以:

  • 使用发行版的工具来监视系统时钟的更改(我不太确定这些工具是什么;最糟糕的是,您可以每5分钟运行一次cron作业,以检查时钟是否在其期望值附近)。< / li>
  • 在检测到系统时钟更改后,将一些信息传达给您拥有等待/睡眠线程的进程。您可能会使用信号;或管道;或unix域套接字;甚至一些共享内存。
  • 在进程方面,请确保您收到此消息(即,编写信号处理程序,或让线程在管道上执行阻塞I / O;或使用非std::condition_variable睡眠来轮询共享内存-分别)
  • 收到有关系统时钟更改的通知后,请在处理过程中进行调整,唤醒睡眠线程并根据更改的时间重新评估需要执行的操作。也许与以前完全一样,在这种情况下,您只需再次使用条件变量即可让线程运行。

不是很优雅,而且涉及很多开销-但这确实是有道理的,并且不是一些疯狂的技巧。