jac*_*bsa 13 c++ atomic memory-model
标准中是否有任何措辞可以保证放松存储到原子的位置不会超过锁定互斥锁?如果没有,是否有任何措辞明确表示编译器或CPU是否可以这样做?
例如,采取以下计划:
std::mutex mu;
int foo = 0; // Guarded by mu
std::atomic<bool> foo_has_been_set{false};
void SetFoo() {
mu.lock();
foo = 1;
foo_has_been_set.store(true, std::memory_order_relaxed);
mu.unlock();
}
void CheckFoo() {
if (foo_has_been_set.load(std::memory_order_relaxed)) {
mu.lock();
assert(foo == 1);
mu.unlock();
}
}
Run Code Online (Sandbox Code Playgroud)
CheckFoo
如果另一个线程同时调用SetFoo
,是否有可能在上述程序中崩溃,或者是否有一些保证不能将存储foo_has_been_set
提升到mu.lock
编译器和CPU 的调用之上?
这与一个较旧的问题有关,但我并不是100%明白,答案适用于此.特别是,该问题的答案中的反例可能适用于两个并发调用SetFoo
,但我对编译器知道有一个调用SetFoo
和一个调用的情况感兴趣CheckFoo
.这是否保证安全?
我正在寻找标准中的具体引用.谢谢!
我想我已经找到了保证程序不会崩溃的特定偏序边.在下面的答案中,我引用了标准草案的N4659版本.
编写器线程A和读者线程B涉及的代码是:
A1: mu.lock()
A2: foo = 1
A3: foo_has_been_set.store(relaxed)
A4: mu.unlock()
B1: foo_has_been_set.load(relaxed) <-- (stop if false)
B2: mu.lock()
B3: assert(foo == 1)
B4: mu.unlock()
Run Code Online (Sandbox Code Playgroud)
我们寻求证明如果B3执行,那么A2发生在B3之前,如[intro.races]/10中所定义.通过[intro.races] /10.2,证明A2线程在B3之前发生就足够了.
因为给定互斥锁上的锁定和解锁操作只发生在一个总顺序中([thread.mutex.requirements.mutex]/5),所以我们必须首先使用A1或B2.这两种情况:
假设A1发生在B2之前.然后通过[thread.mutex.class]/1和 [thread.mutex.requirements.mutex]/25,我们知道A4将与B2同步.因此,通过[intro.races] /9.1,A4线程发生在B2之前.由于B2在B3之前排序,通过[intro.races] /9.3.1我们知道A4线程发生在B3之前.由于A2在A4之前排序,因此通过[intro.races] /9.3.2,A2线程发生在B3之前.
假设B2发生在A1之前.然后通过与上面相同的逻辑,我们知道B4与A1同步.因此,由于A1在A3之前排序,通过 [intro.races] /9.3.1,B4线程发生在A3之前.因此,由于B1在B4之前被排序,因此通过[intro.races] /9.3.2,B1线程发生在A3之前.因此,通过[intro.races] /10.2,B1发生在A3之前.但是根据 [intro.races]/16,B1必须从前A3状态获取其值.因此,负载将返回false,并且B2将永远不会首先运行.换句话说,这种情况不可能发生.
因此,如果B3完全执行(情况1),则A2发生在B3之前,并且断言将通过.∎
归档时间: |
|
查看次数: |
743 次 |
最近记录: |