c++ - boost 与标准原子顺序一致性语义

标签 c++ c++11 boost atomic memory-model

我想编写一个 C++ 无锁对象,其中有许多记录器线程记录到一个大型全局(非原子)环形缓冲区,偶尔有一个读取器线程想要读取缓冲区中尽可能多的数据可能的。我最终得到了一个全局原子计数器,记录器在其中获取要写入的位置,并且每个记录器在写入之前自动递增计数器。读取器尝试读取缓冲区和每个记录器的本地(原子)变量,以了解特定缓冲区条目是否正忙于由某个记录器写入,以避免使用它们。

所以我必须在纯读取器线程和许多写入器线程之间进行同步。我觉得不用锁也能解决问题,我可以依靠“happens after”关系来判断我的程序是否正确。

我试过宽松的原子操作,但它不会工作:原子变量存储是释放,加载是获取,并且保证某些获取(及其后续工作)总是“发生在”某些释放(和它之前的工作)。这意味着读取器线程(根本不进行任何存储)无法保证在读取缓冲区后“发生”某些事情,这意味着我不知道某些记录器是否已经覆盖了缓冲区的一部分,当线程正在读取它。

所以我转向顺序一致性。对我来说,“原子”意味着 Boost.Atomic,顺序一致性的概念有一个“模式”documented :

The third pattern for coordinating threads via Boost.Atomic uses seq_cst for coordination: If ...

  1. thread1 performs an operation A,
  2. thread1 subsequently performs any operation with seq_cst,
  3. thread1 subsequently performs an operation B,
  4. thread2 performs an operation C,
  5. thread2 subsequently performs any operation with seq_cst,
  6. thread2 subsequently performs an operation D,

then either "A happens-before D" or "C happens-before B" holds.

请注意,第二行和第五行说的是“任何操作”,而没有说明它是否修改任何内容,或者它对什么进行操作。这提供了我想要的保证。

一切都很开心,直到我看到 Herb Sutter 的题为“atomic<> Weapnos”的演讲。他的意思是 seq_cst 只是一个 acq_rel,具有一致的原子存储顺序的额外保证。我转向cppreference.com , 具有相似的描述。

所以我的问题:

  1. C++11 和 Boost Atomic 是否实现相同的内存模型?
  2. 如果 (1) 为"is",是否意味着 Boost 描述的“模式”以某种方式被 C++11 内存模型暗示?如何?或者这是否意味着 cppreference 中 Boost 或 C++11 的文档是错误的?
  3. 如果 (1) 是“否”,或 (2) 是“是,但 Boost 文档不正确”,有没有办法在 C++11 中实现我想要的效果,即保证(一些原子存储发生在(之前的工作)一些原子加载之后?

最佳答案

我在这里看到没有答案,所以我在 Boost 用户邮件列表中再次询问。 我在那里也没有看到答案(除了建议调查 Boost lockfree),所以我打算问问 Herb Sutter(期待没有答案 反正)。但在这样做之前,我用谷歌搜索了一下“C++ 内存模型” 更深入。读完汉斯·伯姆的一页之后 ( http://www.hboehm.info/c++mm/ ),我可以自己回答大部分问题 问题。我在谷歌上搜索了更多,这次是“C++ Data Race”,并且 登陆 Bartosz Milewski 的页面 (http://bartoszmilewski.com/2014/10/25/dealing-with-benign-data-races-the-c-way/)。 然后我可以回答更多我自己的问题。不幸的是,我仍然 鉴于这些知识,我不知道如何做我想做的事。也许 我想做的事情在标准 C++ 中实际上是无法实现的。

我的问题的第一部分:“C++11 和 Boost.Atomic 是否实现了 相同的内存模型?”答案大多是"is"。我的第二部分 的问题:“如果(1)是'是',它是否意味着“模式” Boost 所描述的 C++11 内存模型以某种方式暗示了这一点?” 答案又是,是的。 “如何?”由此处找到的证明回答 (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2392.html)。 本质上,对于无数据竞争的程序,添加的一点点 acq_rel 足以保证 seq_cst 所需的行为。 因此,这两个文档虽然可能令人困惑,但都是正确的。

现在真正的问题是:虽然 (1) 和 (2) 都得到"is"的答案,但我的 原来的程序是错误的!我忽略了(实际上,我不知道)一个 C++ 的重要规则:具有数据竞争的程序具有未定义的行为 (而不是“未指定”或“实现定义”的)。那 是,编译器保证我的程序的行为只有当我的程序 绝对没有数据竞争。没有锁,我的程序包含一个 数据竞争:纯读取线程可以随时读取,甚至是一次读取 当记录器线程忙于写入时。这是“未定义的行为”, 规则说计算机可以做任何事情(“着火” 规则)。要修复它,必须使用在 Bartosz 的页面中找到的想法 我之前提到的 Milewski,即更改环形缓冲区以包含 只有原子内容,以便编译器知道它的顺序是 重要的,不能用标记为的操作重新排序 要求顺序一致性。如果需要最小化开销, 可以使用宽松的原子操作对其进行写入。

不幸的是,这也适用于读者线程。我不能再 只是“memcpy”整个内存缓冲区。相反,我还必须使用 轻松的原子操作来读取缓冲区,一个字一个字地读。 这会降低性能,但实际上我别无选择。幸运的是 我,自卸车的表现对我来说根本不重要:它很少 无论如何都会运行。但是如果我确实想要“memcpy”的性能,我 会得到“无解决方案”的答案:C++ 不提供“I 知道有数据竞争,你可以在这里把任何东西还给我,但不要 搞砸我的程序”。要么你确保没有数据竞争 并支付费用以明确定义所有内容,或者您​​有数据 种族和编译器被允许把你关进 jail 。

关于c++ - boost 与标准原子顺序一致性语义,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29553181/

相关文章:

c++ - C++ 中的复合类型、const 和 auto

multithreading - 为什么在 GCC 和 Clang 中使用 std::thread 需要 -pthread?

c++ - 使用 MSVC 2015 构建 boost 1.55 时指定工具集版本

c++ - 将 boost::shared_lock 升级为独占锁

c++ - 为什么在全局变量的析构函数中调用 thread.join 会失败

c++ - SDL 应用程序使用 DirectFB 错误 : No available video device

c++ - 确定基于四元数调整方向所需的角速度

c++ - 根据常量整数字段设置数组字段的长度

c++ - 声明/定义自定义类 cout 对象的正确方法

boost - 在ubuntu上 boost intel icpc编译器错误