没有原子的 C++ 线程安全 [关闭]

Posted

技术标签:

【中文标题】没有原子的 C++ 线程安全 [关闭]【英文标题】:C++ thread safety without atomics [closed] 【发布时间】:2021-05-25 10:54:01 【问题描述】:

我知道atomics为多线程程序提供了保证,但是在下面这种情况下,如果我不使用atomics有什么问题吗?

struct Foo  
  Foo() : a(0) 
    findA(&a);
  

  int a;


void findA(int *a) 
  // spaws a thread...
  // the following runs on a separate thread
  *a = 5; // find the actual value of a (5 for example)

一旦构造函数完成,其他线程将永远不会写入a,所以我可以在不锁定的情况下安全地读取/写入它吗?

【问题讨论】:

您可以安全地读写它,就像非多线程程序可以读写任何对象或变量一样安全。 当您说“没有其他线程将写入a”时,这是否包括运行Foo 构造函数的线程和对findA 的调用?如果它在新线程运行时访问a,您仍然有数据竞争。 @Someprogrammerdude 只有运行构造函数的主线程才会读/写它(我们在这个线程中使用Foo对象) 创建一个minimal reproducible example 可能更容易判断它是否安全。 findA 产生的线程是否在 之前加入? 【参考方案1】:

C++ 内存模型指出,如果一个线程写入一个变量而另一个线程读取,而这些操作没有按顺序进行,则根本无法保证程序的操作。

有很多方法可以顺序访问;原子只是其中之一。但是这种 UB 在幼稚的多线程代码中真的很容易触发。

该规则的目标是允许推理单线程本地状态。例如,如果一个线程从a 读取7,并且它知道它没有进行同步,也没有任何可以编辑a 的操作,它不必从内存中重新加载它,清除cpu 缓存,或任何那个开销。它会知道值仍然是7

【讨论】:

好吧,原子是一种有保证的方法,但我不想使用原子,因为它适用于结构的成员,我不希望外部代码知道这一点。那么我可以只使用常规成员变量并在需要保证的地方手动插入内存围栏吗?因为另外,我的变量大多是整数,并且因为对整数的读/写已经是原子的,所以我可以直接设置变量并插入栅栏来保证? @jojo 线程安全是一个关系属性。一个操作相对于另一个操作是线程安全的。如果一个线程正在读取“普通”内存,它不知道另一个线程可以修改,我不知道修改该数据的方法不会导致 C++ 内存模型中的 UB。所以我没有头绪。您可能不知道您需要分享的详细信息来询问谁的答案将解决您的问题。答案可能是“你不能那样做”。 参与问题 cmets 并听从他们的建议。 mcve 是关键。

以上是关于没有原子的 C++ 线程安全 [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

C++多线程1.2-线程安全的保证——互斥量mutex(锁)和原子变量atomic

多线程带来的风险——线程安全

多线程带来的风险——线程安全

线程安全

线程安全—原子性

java 导致多线程数据安全问题的原因