c++ - 系统时间更改时 wait_until 的 condition_variable 解决方法
问题描述
我有一个计时器类,它使用std::condition_variable
wait_until
(我也试过wait_for
)。我正在使用std::chrono::steady_clock time
等到未来的特定时间。
这意味着是单调的,但这是一个长期存在的问题,它实际上使用系统时钟并且在系统时间更改时无法正常工作。
它已按照此处的建议在 libc 中修复:https ://gcc.gnu.org/bugzilla/show_bug.cgi?id=41861 。
问题是这仍然很新~2019 并且仅在 gcc 版本 10 中可用。我有一些仅高达 gcc 版本 ~8 的交叉编译器。
如果有办法将此修复程序添加到我的 gcc 版本(我有很多交叉编译器)中,我会徘徊吗?- 但是如果我每次更新交叉编译器时都必须重新构建它们,这可能很难维护。
所以一个更好的问题可能是,在我将所有工具都升级到 gcc v10 之前,这个问题的解决方案是什么?- 我怎样才能让我的计时器抵抗系统时间的变化?
更新笔记
- Rustyx 提到所需的 glibc 版本是 2.3.0+(更多供我参考 - 用于
ldd --version
检查) - glibc 更改日志显示了 Daniel Langr 提供的修复的相关条目:https ://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/ChangeLog-2019#L2093
- 所需的 glibc 补丁(由 rustyx 提供):https ://gcc.gnu.org/git/?p=gcc.git&a=commit;h=ad4d1d21ad5c515ba90355d13b14cbb74262edd2
解决方案
创建一个数据结构,其中包含一个条件变量列表,每个条件变量都有一个受互斥体保护的使用计数。
当线程即将阻塞条件变量时,首先获取互斥锁并将条件变量添加到列表中(如果它已经在列表中,则增加其使用计数)。
在条件变量上完成阻塞后,让线程再次获取保护列表的互斥锁并减少它被阻塞的条件变量的使用计数。如果条件变量的使用计数降至零,则从列表中删除条件变量。
有一个专门的线程来观察系统时钟。如果它检测到时钟跳跃,则获取保护条件变量列表的互斥锁并广播每个条件变量。
而已。这样就解决了问题。
如有必要,您还可以为表中的每个条目添加一个布尔值,并在添加条目时将其设置为 false。如果时钟观察线程已经广播了条件变量,让它将 bool 设置为 true,这样被唤醒的线程就会知道它们被唤醒的原因。
如果您愿意,您可以在创建条件变量时将其添加到列表中,并在销毁时将其从列表中删除。如果时钟跳跃,这将导致广播条件变量没有线程被阻塞,但这是无害的。
以下是一些实施建议:
使用专用线程查看时钟。一个容易看的事情是墙上时间和系统正常运行时间之间的偏移量。
要做的一件简单的事情是记录观察到的时间跳跃次数,并在每次感觉到时间跳跃时增加它。等待条件时,可以使用以下逻辑:
- 注意时间跳跃的次数。
- 封锁条件。
- 当你醒来时,重新检查情况。
- 如果条件不满足,检查时间跳跃的次数。
- 如果 1 和 4 的计数不匹配,则将其作为时间跳转唤醒处理。
你可以把这一切都包装起来,这样调用代码就没有丑陋了。它只是您的wait_for版本的另一个可能的返回值。
推荐阅读
- django - 如何在 Django 中获取 Api Body 参数
- javascript - 使用 JavaScript 的成本计算器 - 高级
- javascript - 如何将 Actions 信息传递给组件、React + Redux?
- bash - bash if 语句没有按预期工作
- mule - 如何使用 Mule 4 的默认 amqp 交换发布 amqp 消息
- xml - 我的 xslt 没有显示段落中的换行符
- html - 如何在高度未知的地方垂直和水平设置div?
- sql - 如何使用 Powershell 处理 sql_Variant sql 数据类型
- c++ - 使用 qmake 编译的应用程序如果从控制台编译则不会运行,但如果从 QtCreator 编译则运行
- jquery - JQUERY $(document).click vs $(document).ready