考虑以下取自 Herb Sutter 关于原子的演讲的代码片段:
smart_ptr 类包含一个名为 control_block_ptr 的 pimpl 对象,其中包含引用计数refs。
// Thread A:
// smart_ptr copy ctor
smart_ptr(const smart_ptr& other) {
...
control_block_ptr = other->control_block_ptr;
control_block_ptr->refs.fetch_add(1, memory_order_relaxed);
...
}
// Thread D:
// smart_ptr destructor
~smart_ptr() {
if (control_block_ptr->refs.fetch_sub(1, memory_order_acq_rel) == 1) {
delete control_block_ptr;
}
}
Herb Sutter 说线程 A 中refs的增量可以使用 memory_order_relaxed 因为“没有人根据动作做任何事情”。现在,据我了解 memory_order_relaxed,如果refs在某个时候等于 N,并且两个线程 A 和 B 执行以下代码:
control_block_ptr->refs.fetch_add(1, memory_order_relaxed);
那么可能会发生两个线程都看到refs的值为N 并且都将 N+1 写回它的情况。这显然行不通,应该像使用析构函数一样使用 memory_order_acq_rel。我哪里错了?
EDIT1:考虑以下代码。
atomic_int refs = N; // at time t0.
// [Thread 1]
refs.fetch_add(1, memory_order_relaxed); // at time t1.
// [Thread 2]
n = refs.load(memory_order_relaxed); // starting at time t2 > t1
refs.fetch_add(1, memory_order_relaxed);
n = refs.load(memory_order_relaxed);
在调用 fetch_add 之前,线程 2 观察到的refs的值是多少?可以是 N 还是 N+1?调用 fetch_add 后线程 2 观察到的 refs 的值是多少?必须至少为 N+2 吗?
[谈话网址:C++ & Beyond 2012 - http://channel9.msdn.com/Shows/Going+Deep/Cpp-and-Beyond-2012-Herb-Sutter-atomic-Weapons-2-of-2 (@ 1: 20:00)]