【问题标题】:Implicit synchronization when creating/joining threads创建/加入线程时的隐式同步
【发布时间】:2015-04-16 19:34:38
【问题描述】:

考虑到创建/加入线程时的隐含同步:std::atomic 时,x 的类型需要什么最小帧才能工作? volatile?什么都没有?

#include <thread>
#include <cassert>
int main() {
    int x = 123; // ***
    std::thread( [ & ] { assert( x == 123 ); x = 321; } ).join();
    assert( x == 321 );
    return 0;
}

【问题讨论】:

  • 您需要阅读this answer 来了解相关问题。
  • std::thread( [ &amp; ] { assert( x == 123 ); x = 321; } ).join(); x 没有并发访问,您可以按顺序调用 lambda 以实现相同的行为。 volatile 从不用于线程安全,顺便说一句。

标签: c++ multithreading language-lawyer memory-model


【解决方案1】:

std::thread 的构造函数的调用是同步的,发生在线程函数的副本调用之前 (30.3.1.2/6)。

thread::join 提供了类似的同步保证:线程的完成发生在join 返回 (30.3.1.4/7) 之前。

您的代码创建一个线程并立即加入它。尽管您的 lambda 通过引用捕获,但没有并发(代码按顺序运行),并且std::thread 提供的保证确保您不需要任何特殊框架来保护x。断言永远不会失败。

假设您的代码 sn-p 是不同的,因此您实际上具有某种并发访问权限,您将不得不使用 std::atomic 或互斥锁。 volatile 肯定是不够的(巧合除外)。

【讨论】:

  • 我知道按照规范 volatile 是不够的,因为只有 std::atomic 类型被声明为无数据竞争,但 int 到底是什么? ,这可能使它变得活泼吗?我如何识别失败?是不是因为在某些平台上 int 的四个字节可以写成两个 2 字节的块,而在其他具有本机 32 位 int 的平台上,即使它仍然是错误的,它也会碰巧总是工作?在某些平台上,std::atomic&lt;int&gt; 可以只是int 的别名吗?这里是真正好奇的学习者。
  • @antiduh 这不仅仅是在单独读取或写入该变量。使用原子也会限制重新排序。
  • @antiduh:原子类型及其专业化保证您有一定的(无论您选择哪个)排序保证。也就是说,例如,在原子操作之前,保证依赖写入(或独立写入)是全局可见的。 volatile 不提供此类保证。它可能“有效”,但这是巧合。关于写出整数,假设主流 CPU 和正确对齐,你是对的。非原子int 写入是不可能的。但是,这并没有正式保证,也不能移植,没有定义的顺序,并且 RMW 操作(例如++x)也是非原子的。
猜你喜欢
  • 2014-12-06
  • 1970-01-01
  • 2022-01-11
  • 1970-01-01
  • 2011-05-08
  • 2012-04-22
  • 2017-04-27
  • 2021-05-08
  • 1970-01-01
相关资源
最近更新 更多