【问题标题】:condition_variable workaround for wait_until with system time change系统时间更改时 wait_until 的 condition_variable 解决方法
【发布时间】:2020-12-17 06:43:39
【问题描述】:

我有一个计时器类,它使用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 之前,有什么解决方案可以解决这个问题? - 如何使我的计时器能够抵抗系统时间变化?

更新笔记

【问题讨论】:

  • 我相信这是 libstdc++ 的问题,而不是编译器本身。您不能独立于 GCC 升级您的“系统”libstdc++ 版本吗?这是link to changelog
  • 您需要 glibc 2.3.0+ (pthread_cond_clockwait) 并使用提供的 patch 修补您的 libstdc++
  • @DanielLangr 我在这里可能是错的,但 libc++ 不是构建工具包的一部分 - 我认为它是 gcc 附带的吗? - 在链接中它说它在 gcc v10 中是固定的(页面的最后一行)。将 libc++ 更改为其他版本可能会破坏很多东西吗? - 我没有真正尝试过这个,除了一个非常小的变化(比如从 libc++ v6.0.1 到 v6.0.2 - 不是实数!)
  • @code_fodder 我认为您始终可以构建自己的 libstdc++ 版本并告诉 GCC 使用它而不是 GCC 附带的系统默认版本/版本。
  • @DanielLangr 所以我只是看看我的目标设置,我们正在使用 bitbake 创建 rootfs(其中包含 glibc 等),所以我认为我只是需要修补我的 bitbake 包中的 glibc 版本,因为它包含所有源代码,我只需要它在目标上工作。在我的本机 linux 发行版上重建 gllibc 可能更难,因为我没有设置这样做:o - 感谢两者,我认为这给了我正确的探索方向。请随时更新为答案

标签: c++ linux gcc condition-variable


【解决方案1】:
  1. 创建一个包含条件变量列表的数据结构,每个条件变量的使用计数受互斥体保护。

  2. 当线程即将阻塞条件变量时,首先获取互斥锁并将条件变量添加到列表中(如果它已经在列表中,则增加其使用计数)。

  3. 在条件变量上完成阻塞后,让线程再次获取保护列表的互斥锁并减少它被阻塞的条件变量的使用计数。如果条件变量的使用次数降至零,则将其从列表中删除。

  4. 有一个专门的线程来监视系统时钟。如果检测到时钟跳变,则获取保护条件变量列表的互斥体并广播每个条件变量。

就是这样。这样就解决了问题。

如有必要,您还可以为表中的每个条目添加一个布尔值,并在添加条目时将其设置为 false。如果时钟观察线程已经广播了条件变量,让它将 bool 设置为 true,这样被唤醒的线程就会知道它们被唤醒的原因。

如果您愿意,您可以在创建条件变量时将其添加到列表中,并在销毁时将其从列表中删除。如果时钟跳变,这将导致广播条件变量没有线程被阻塞,但这是无害的。

以下是一些实施建议:

使用专用线程查看时钟。一个容易查看的是挂钟时间和系统正常运行时间之间的偏移量。

要做的一件简单的事情是记录观察到的时间跳跃次数,并在每次感觉到时间跳跃时递增。等待条件时,可以使用如下逻辑:

  1. 记下跳跃的次数。
  2. 按条件屏蔽。
  3. 醒来后,重新检查病情。
  4. 如果条件不满足,请检查跳转次数。
  5. 如果 1 和 4 的计数不匹配,则将其作为时间跳转唤醒处理。

您可以将这一切都包装起来,这样调用代码中就没有丑陋的东西了。它只是您的wait_for 版本的另一个可能的返回值。

【讨论】:

  • 出于好奇,您打算使用哪种算法来检测时钟跳动?
  • 我确实有一个粗略的备份计划,即拥有一个带有指向所有实例的指针的静态成员变量以及一个用于唤醒所有线程的静态函数。这应该可行 - 但它需要外部输入,我想避免这种情况 - 但它是一个合理的解决方法:)
  • @darune 对我来说这不是问题会是另一堆工作,听起来很难做?!
  • @darune 我不关心少于一秒的跳跃。所以我的代码每秒检查一次时钟。如果它不在预期的一秒内,我们假设有一个跳跃。您还可以使用正常运行时间时钟和挂钟之间的偏移量。
  • @code_fodder 检查时钟的情况并不困难。只需创建一个线程,其唯一目的是看时钟。只要需要,线程就会唤醒并检查挂起时间和正常运行时间之间的偏移量。如果它已经跳转,则将跳转记录在其他线程可以轻松找到的地方。如果您使用的是现代版本的 C++,您可以轻松地封装所有这些,以便代码在任何地方都是干净的,但在此实现中。
猜你喜欢
  • 1970-01-01
  • 2023-03-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多