有啥可以使静态布尔线程安全的吗?

Posted

技术标签:

【中文标题】有啥可以使静态布尔线程安全的吗?【英文标题】:Is there anything that would make a static bool thread safe?有什么可以使静态布尔线程安全的吗? 【发布时间】:2021-09-20 00:49:33 【问题描述】:

我最近遇到了一些运行良好的代码,其中 static bool 在多个线程(单个写入器,多个接收器)之间共享,尽管没有同步。

类似的东西(简化):

//header A
struct A 
   static bool f;
   static bool isF()  return f; 
;

//Source A
bool A::f = false;

void threadWriter()
    /* Do something */
    A::f = true;


// Source B
void threadReader()
   while (!A::isF())  /* Do something */

对我来说,这种代码有一个竞争条件,即使 bool 上的操作是原子的(在大多数 CPU 上),我们不能保证来自写入线程的写入对读取线程是可见的。但有些人告诉我,f 是static 会有所帮助。

那么,C++11 中有什么东西可以使这段代码安全吗?或者任何与静态相关的东西可以使这段代码工作?

【问题讨论】:

不,这段代码会导致 UB。 std::atomic?此外,即使您的 bool 是原子的,调用 isF() 来读取它也可能不是。 这是一种老派的态度,可能并不准确,但我一直坚信,如果它需要线程安全,就需要互斥体。我会关注是否有人发布更好的答案。 @JosephLarson -- std::atomic<T> 在单个对象上提供线程安全操作。如果这足够了,就不需要互斥体,而且原子操作可能会更快。 其实我不太明白这个问题。是不是“这段代码有异味,但它有效。为什么?”还是“这段代码有异味,如何解决?” 【参考方案1】:

您的硬件可能能够以原子方式在bool 上运行。但是,这并不能使此代码安全。就 C++ 而言,您在不同的线程中写入和读取 bool 没有同步,这是未定义的。

bool 设为静态不会改变这一点。

要以线程安全的方式访问bool,您可以使用std::atomic<bool>atomic 是否使用互斥锁或其他锁定取决于实现。

尽管std::atomic<bool> 也不足以同步threadReader()threadWriter(),以防每个/*Do something */ 访问相同的共享数据。


但有些人告诉我,f 是静态的这一事实会有所帮助。

坦率地说,这听起来像是对货物的***。我可以想象这与静态局部变量的初始化是线程安全的事实混淆了。来自cppreference:

如果多个线程尝试初始化相同的静态本地 变量并发,初始化只发生一次(类似 可以使用 std::call_once 获得任意函数的行为。

注意:此功能的通常实现使用 双重检查锁定模式,它减少了运行时开销 已经初始化的局部静态变量为单个非原子布尔值 比较。

查找 Meyers 单例以查看该示例。虽然,这仅仅是关于初始化。例如这里:

 int& foo() 
     static int x = 42;
     return x;
 

两个线程可以同时调用这个函数,x 将被初始化一次。这对x 本身的线程安全没有影响。如果两个线程调用foo,一个写入另一个读取x,则存在数据竞争。但是,这是关于静态局部变量的初始化,与您的示例无关。当他们告诉你static 会“帮助”时,我不知道他们是什么意思。

【讨论】:

这都是正确的。但即使使用std::atomic<bool>,代码也包含其他错误。使用std::atomic<bool> 并不能充分防止threadWriterthreadReader 之间的竞争条件。一方面,threadWriter 需要以某种方式等待没有活动的threadReader @FrançoisAndrieux 没有使用storeload 足够的同步?抱歉,我不确定我是否完全理解代码(以及您的评论) 这里的关键是“hardware may be able to atomically operation on a bool”中的“atomically”与std::atomic<bool>中的“atomically”不一样,支持在@987654348上进行原子操作@ 目的。 std::atomic 从 C++ 语言的角度管理潜在的数据竞争,并确保没有数据竞争。 @PeteBecker 这基本上就是我想说的,只是措辞更好;) @463035818_is_not_a_number 足以避免UB访问flag。但是问题中使用的方案不会同步threadWriterthreadReader 操作的数据(假设它们接触相同的数据)。如果一个线程调用threadWriter,而另一个线程正在运行threadReader,那么没有什么可以阻止共享数据的竞争。

以上是关于有啥可以使静态布尔线程安全的吗?的主要内容,如果未能解决你的问题,请参考以下文章

单例实例声明为 GetInstance 方法的静态变量,它是线程安全的吗? [复制]

单例实例声明为 GetInstance 方法的静态变量,它是线程安全的吗? [复制]

如果不修改静态类变量,非同步静态方法是不是线程安全?

线程安全和静态函数

lua引擎本身是线程安全的吗

我可以假设 Delphi NOW 函数是线程安全的吗?