C++11 内存模型详解(续)(3)

现在我们尝试用 acquire/release 语义来解决它,假设 thread 0 先执行并进入了临界区,然后 thread 1 后执行,当 thread 1 执行到 #6 时,怎么保证能看到 thread 0 对 flag0 的修改呢?根据前面第二节的介绍,我们知道关键在于要保证 #1 happen-before #6,又由于 #1 和 #6 分别在不同的线程,因此其实就是要保证 #1 synchronize-with #6,因此我们需要在 thread 0 中以 release 的方式写一个变量 A,然后在 thread 1 中以 acquire 的方式读取该变量 A,那么我们应该选取哪个变量作为这个关键变量呢?

flag0 不行,原因与前面第一节的例子相同,flag0 是我们要读取的关键变量,我们要保证的是能读取到它的最新值,而不是通过它来实现 synchronize-with.

flag1 也不行,flag1 在 thread 0 只有一个 load 操作,没有 release 语义(但如果用 flag1.fetech_add(0, memory_order_acq_rel) 呢?应该也是行的,只是不是最好,多了一次无谓的写操作)。

最优选择应该是 turn 变量。

因此得到如下解法如下:

// Thread 0 flag0.store(true, memory_order_relaxed); // 1 r0 = turn.exchange(1, memory_order_acq_rel); // 2 r1 = flag1.load(memory_order_acquire); // 3 // Thread 1 flag1.store(true, memory_order_relaxed); // 4 r0 = turn.exchange(2, memory_order_acq_rel); // 5 r1 = flag0.load(memory_order_acquire); // 6

让我啰嗦点指出其中一个关键,#2 和 #5 能建立 synchronize-with 关系的关键在于 exchange 是一个 RMW 操作,它的读操作总能够读到变量最新的值(c++ 标准 29.3.12),因此当 thread 0 先执行时,turn 会被以 release 的方式写入一个值,再然后后面 thread 1 执行 #3 ,会以 acquire 的方式对 turn 进行读取,因为 RMW 保证它的 load 会 load 到最新的值,因此此时 #2 synchronize-with #5,皆大欢喜。

C++11新特性:Lambda函数(匿名函数) 

C++ Primer Plus 第6版 中文版 清晰有书签PDF+源代码

读C++ Primer 之构造函数陷阱

读C++ Primer 之智能指针

读C++ Primer 之句柄类

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/a76ff1bb967428d5bcc70754a246c086.html