在等待std :: condition_variable时如何处理系统时钟更改?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在等待std :: condition_variable时如何处理系统时钟更改?相关的知识,希望对你有一定的参考价值。
我正在尝试在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函数并使用单调时钟实现相同的接口。
这听起来像很多工作 - 而且有点混乱。还有其他解决此问题的方法吗?
在考虑了这个问题的可能解决方案之后,似乎最有意义的解决方案是禁止使用std :: condition_variable(或者至少要明确指出它总是会使用系统时钟)。然后我必须以一种尊重时钟选择的方式自己重新实现标准库的condition_variable。
由于我必须支持多个平台(Bionic,POSIX,Windows,最终是MacOS),这意味着我将维护此代码的多个版本。
虽然这很讨厌,但似乎替代方案甚至更糟糕。
集中注册您的活动条件变量。
做一些努力来检测时钟错误,即使它是当前时钟(ick)或其他方式的线程自旋锁定。
当您检测到时钟错误时,请调用条件变量。
现在将条件变量包装在一个瘦包装器中,该包装器也支持检测时钟滑动。它调用wait_until
,但用一个检测到时钟滑点的谓词替换谓词,当发生这种情况时,会突破等待。
当你的实施被打破时,你必须做你必须做的事情。
我遇到了同样的问题。我的一位同事给了我一个使用来自<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_t
和pthread_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);
这可能不是最好的解决方案,也不是一个很好的解决方案,但你确实说“解决”而不是“很多工作”,所以:
- 使用您的发行版设施来监控系统时钟的变化(我不太确定这些设施是什么;最糟糕的是,您可以每5分钟运行一次cron作业来检查时钟是否接近其预期值)。
- 检测到系统时钟更改后,将某些内容传递给您有等待/休眠线程的进程。你可能会使用一个信号;或管道;或者是unix-domain套接字;甚至是一些共享内存。
- 在进程'一边,确保你收到这个(即写一个信号处理程序,或者让一个线程在管道上阻塞I / O;或者分别使用非
std::condition_variable
sleep轮询共享内存) - 在收到有关系统时钟更改的通知后,在您的过程中动摇,唤醒睡眠线程,并根据更改的时间重新评估需要完成的操作。也许它和以前完全一样,在这种情况下,你只需要让你的线程再次使用condition变量。
不是很优雅,并且涉及到一大堆开销 - 但这确实有意义,而且不是一些疯狂的黑客攻击。
以上是关于在等待std :: condition_variable时如何处理系统时钟更改?的主要内容,如果未能解决你的问题,请参考以下文章
std::atomic 和 std::condition_variable 等待、notify_* 方法之间的区别
在 Windows 中等待 std::atomic<int> 的正确方法?
使用 shared_from_this 参数等待 std::future 获取 std::async 会阻止对 this 的破坏