std::memory_order
2022年1月16日
字数统计:1072
std::memory_order 是一个枚举类,用于指示编译器进行内存同步所使用的规则,是原子操作的基础。std::memory_order 本质上是C++对于内存一致性的同步操作进行的一种抽象,使用不同的order代表进行不同的同步操作。
enum class memory_order : /*unspecified*/ {
relaxed , consume , acquire , release , acq_rel , seq_cst
};
宽松顺序relaxed
宽松顺序仅保证原子性,不保证内存或者CPU cache的任何同步。
例如:
x = y = 0 ;
// 线程1 :
r1 = y . load ( std :: memory_order_relaxed ); // A
x . store ( r1 , std :: memory_order_relaxed ); // B
// 线程2 :
r2 = x . load ( std :: memory_order_relaxed ); // C
y . store ( 42 , std :: memory_order_relaxed ); // D
由于是宽松顺序,所以允许D -> A -> B -> C。
宽松内存顺序的典型使用是计数器自增,例如 std::shared_ptr 的引用计数器,因为这只要求原子性,但不要求顺序或同步(注意 std::shared_ptr 计数器自减要求与析构函数进行获得释放同步)。
释放获得顺序release acquire
若线程A中的一个原子存储带标签 memory_order_release ,而线程B中来自同一变量的原子加载带标签 memory_order_acquire ,则从线程A的视角先发生于原子存储的所有内存写入(非原子及宽松原子的)在线程B中成为可见副效应,即一旦原子加载完成,则保证线程B能观察到线程A写入内存的所有内容。
同步仅建立在释放和获得同一原子对象的线程之间。其他线程可能看到与被同步线程的一者或两者相异的内存访问顺序。
互斥锁(例如 std::mutex 或原子自旋锁)是释放获得同步的例子:线程A释放锁而线程B获得它时,发生于线程A环境的临界区(释放之前)中的所有事件,必须对于执行同一临界区的线程B (获得之后)可见。
#include <thread>
#include <atomic>
#include <cassert>
#include <string>
std :: atomic < std :: string *> ptr ;
int data ;
void producer ()
{
auto * p = new std :: string ( "Hello" );
data = 42 ;
ptr . store ( p , std :: memory_order_release );
}
void consumer ()
{
std :: string * p2 ;
while ( ! ( p2 = ptr . load ( std :: memory_order_acquire ))); // 获得后,释放操作前的操作一定可见
assert ( * p2 == "Hello" ); // 绝无问题
assert ( data == 42 ); // 绝无问题
}
int main ()
{
std :: jthread t1 ( producer );
std :: jthread t2 ( consumer );
}
释放消费顺序release consume
C++17开始不推荐使用consume,会自动升级为acquire。
序列一致顺序sequence consistent
seq_ cst的原子操作不仅以与释放/获得顺序相同的方式排序内存(在一个线程中先发生于存储的任何结果都变成进行加载的线程中的可见副效应),还对所有带此标签的内存操作建立单独全序。
若在多生产者-多消费者的情形中,且所有消费者都必须以相同顺序观察到所有生产者的动作出现,则可能必须有序列顺序。
全序列顺序在所有多核系统上要求完全的内存栅栏CPU指令。这可能成为性能瓶颈,因为它强制受影响的内存访问传播到每个核心。
此示例演示序列一直顺序为必要的场合。任何其他顺序都可能触发 assert,因为可能令线程 c 和 d 观测到原子对象 x 和 y 以相反顺序更改。
#include <thread>
#include <atomic>
#include <cassert>
std :: atomic < bool > x { false };
std :: atomic < bool > y { false };
std :: atomic < int > z { 0 };
void write_x ()
{
x . store ( true , std :: memory_order_seq_cst );
}
void write_y ()
{
y . store ( true , std :: memory_order_seq_cst );
}
void read_x_then_y ()
{
while ( ! x . load ( std :: memory_order_seq_cst ))
;
if ( y . load ( std :: memory_order_seq_cst )) {
++ z ;
}
}
void read_y_then_x ()
{
while ( ! y . load ( std :: memory_order_seq_cst ))
;
if ( x . load ( std :: memory_order_seq_cst )) {
++ z ;
}
}
int main ()
{
std :: thread a ( write_x );
std :: thread b ( write_y );
std :: thread c ( read_x_then_y );
std :: thread d ( read_y_then_x );
a . join (); b . join (); c . join (); d . join ();
assert ( z . load () != 0 ); // 决不发生
}
参考
若无特殊声明,本人原创文章以
CC BY-SA 4.0许可协议
提供。
本站不欢迎非搜索引擎类,非个人学习类爬虫;严禁将文章直接爬取至其他站点。
若看到此条消息,说明你正在访问的网站可能是垃圾二手转载网站。
为了获得更好的浏览体验,请访问唯一原始网站:mysteriouspreserve[dot]com或blog[dot]bizwen[dot]com。