【问题标题】: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> 在单个对象上提供线程安全操作。如果这足够了,就不需要互斥体,而且原子操作可能会更快。
  • 其实我不太明白这个问题。是不是“这段代码有异味,但它有效。为什么?”还是“这段代码有异味,如何解决?”

标签: c++ multithreading c++11


【解决方案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,那么没有什么可以阻止共享数据的竞争。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-11-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多