原子布尔变量的一条语句中的多重赋值

Posted

技术标签:

【中文标题】原子布尔变量的一条语句中的多重赋值【英文标题】:Multiple assignment in one statement for atomic bool variables 【发布时间】:2017-10-09 22:14:17 【问题描述】:

从Multiple assignment in one line 开始,我很想知道原子数据类型是如何工作的,尤其是布尔类型的示例。

给定:

class foo 
    std::atomic<bool> a;
    std::atomic<bool> b;
  public:
    void reset();
    [...] //Other methods that might change a and b

两者有什么区别:

void foo::reset() 
  a = false;
  b = false;

还有:

void foo::reset() 
  a = b = false;

即在第二种情况下,会不会发生在b被分配false之后,另一个线程在b之前将b设置为true,以将其值分配给a,所以在指令结束时a 的值是true

(这也意味着后一个版本似乎效率较低)

【问题讨论】:

很难说没有看到实际的编译器输出,但std::atomic::operator=() 应该返回传递给它的值,所以我认为a = ... 不会实际读取@ 的值987654335@,它只会从operator= 的输出中接收false。现在,在多线程情况下,当reset() 退出时,b 仍然可能是true,但这是另一回事。 @RemyLebeau 好吧,那几乎可以回答我的问题,应该没有任何区别,否则编译器将是次优的。谢谢! (当然,reset 的末尾可能是 btrue 如果另一个线程干预) 【参考方案1】:

是的,有区别

a = false;
b = false;

a = b = false;

如果 ab 是原子的。由于赋值是从右到左进行的,所以后者等价于

b = false;
a = false; // since atomic::operator= (from above) returns its argument

这与第一个版本不同,因为ab 是原子的,而assignment is done 就像std::atomic::store 以内存顺序memory_order_seq_cst 调用一样。由此,内存模型guarantees

a single total modification order of all atomic operations that are so tagged.

因此,第二个线程以 reverse 存储顺序 (a = b = false;) 执行原子加载 (bool a_observed = a.load(); bool b_observed = b.load();) 可能会以以下三种方式之一观察变化

ba 的旧值 加载a,加载b存储b存储a b 的新值和a 的旧值 存储b,加载a存储a,加载b 存储b,加载a,加载b存储a 加载a,存储b存储a,加载b 加载a,存储b,加载b存储a ba 的新值 存储b,存储a,加载a,加载b

相比之下,memory_order_seq_cst 用于在a 之前存储b 而(在另一个线程中)在b 之前加载a 保证永远不会观察到以下内容

a 的新值和b 的旧值

【讨论】:

谢谢,我已经为 Clang 3.8 测试了 here【参考方案2】:

Godbolt link

两者之间的差异很小。唯一真正的区别是分配顺序被颠倒了。如果开启优化,则无法区分。

【讨论】:

非常酷的链接和网站!实际上,当使用更复杂的定义时,不同之处在于即使优化后也会保留顺序godbolt.org/g/kozyTp

以上是关于原子布尔变量的一条语句中的多重赋值的主要内容,如果未能解决你的问题,请参考以下文章

PL/SQL流程控制

关于if语句中的小细节

Java多线程之Atomic:原子变量与原子类

在verilog HDL语言中的阻塞赋值和非阻塞赋值究竟有啥不同?同一变量在不同的过程块中(同时触发)又是如

struts中的一条for语句,看不懂,请高手指教。

可以为Groovy中的if语句中的字段赋值可以使数据库更新记录