简单理解c++11内存序

整理自 CPP Concurrency In Action 5.3 Synchronizing operations and enforcing ordering(同步操作和强制排序)

c++11有六个内存序选项可应用于原子类型的操作:memory_order_relaxed、memory_order_consume、memory_order_acquire、memory_order_release、memory_order_acq_rel、memory_order_seq_cst。
代表三种内存模型:顺序一致性(sequentially consistent),获取-释放序(memory_order_consume,memory_order_acquire,memory_order_release,memory_order_acq_rel)和自由序(memory_order_relaxed)。

Store操作,可选如下内存序:memory_order_relaxed, memory_order_release, memory_order_seq_cst。
Load操作,可选如下内存序:memory_order_relaxed, memory_order_consume, memory_order_acquire, memory_order_seq_cst。
Read-modify-write操作,可选如下内存序:memory_order_relaxed, memory_order_consume, memory_order_acquire, memory_order_release, memory_order_acq_rel, memory_order_seq_cst。

极不推荐使用memory_order_consume。个人不推荐使用非默认序,很难考虑周全,很难测试,效率也不见得有明显提升。

顺序一致性

默认使用顺序一致性,顺序一致性中,程序中的行为从任意角度去看,序列都保持一定顺序。每个线程看其他线程操作的顺序都是一样的,比较符合正常思维。
看一个顺序一致性的例子。(后面例子中全局变量x,y,z及其初值均与该例相同,每个函数都是一个线程)

std::atomic<bool> x{false}, y{false};
std::atomic<int> z{0};
// thread 1
void write_x()
{
	x = true; // 1
}
// thread 2
void write_y()
{
	y = true; // 2
}
// thread 3
void read_x_then_y()
{
	while(!x);
	if(y) ++z; // 3
}
// thread 4
void read_y_then_x()
{
	while(!y);
	if(x) ++z; // 4
}

因为序列要保持一定顺序,就能比较简单地分析出所有可能的情况。可以确定的是,1一定在3之前,2一定在4之前,那排列组合左右可能的序列有:2413,2143,2134,1243,1234,1324。z的值分别为1,2,2,2,2,1。

自由序

自由序中,同一线程中对于同一变量的操作还是遵从先行关系,但别的线程看来就不一定了。自由序中唯一要求是同一线程中同一原子变量的访问不能乱序。

为了了解自由序是如何工作的,可先将每一个变量想象成在一个独立房间中拿着记事本的人。他的记事本上是一组值的列表,可以通过打电话的方式让他给你一个值,或让他写下一个新值。如果告诉他写下一个新值,他会将这个新值写在表的最后。如果让他给你一个值,他会从列表中读取一个值给你。
第一次与这人交谈时,如果问他要一个值,他可能会在现有的列表中选取任意值告诉你。
如果之后再问他要一个值,可能会得到与之前相同的值,或是列表中之前值下端的其他值,他不会给你上端的值。
如果让他写一个值,并且随后再问他要一个值,他要不就给你你刚告诉他的那个值,要不就是一个列表中那个值下端的值(别人告诉他写下的值)。
“他”就是使用自由序的原子变量。
多个使用自由序的原子变量,每一个都拥有自己的修改顺序,但是他们之间没有任何关系。
可一个自由序的例子:

void write_x_then_y()
{
	x.store(true, std::memory_order_relaxed); // 1
	y.store(true, std::memory_order_relaxed); // 2
}
void read_y_then_x()
{
	while(!y.load(std::memory_order_relaxed)); // 3
	if(x.load(std::memory_order_relaxed)) // 4
		++z;
}

线程1看来,x、y依次置为true,但在线程2看来,y为true时,x可能为false。

获取-释放序

获取-释放序是自由序的加强版,虽然操作依旧没有统一顺序,但引入了同步。
将上面的例子改成:

void write_x_then_y()
{
	x.store(true, std::memory_order_relaxed); // 1
	y.store(true, std::memory_order_release); // 2
}
void read_y_then_x()
{
	while(!y.load(std::memory_order_acquire)); // 3
	if(x.load(std::memory_order_relaxed)) // 4
		++z;
}

2同步于3,1先于2,3先于4,所以1先于4,z为1。

将顺序一致性中的例子修改下:

void write_x()
{
	x.store(true, std::memory_order_release); // 1
}
void write_y()
{
	y.store(true, std::memory_order_release); // 3
}
void read_x_then_y()
{
	while(!x.load(std::memory_order_acquire)); // 2
	if(y.load(std::memory_order_acquire)) // 5
		++z;
}
void read_y_then_x()
{
	while(!y.load(std::memory_order_acquire)); // 4
	if(x.load(std::memory_order_acquire)) // 6
		++z;
}

1同步于2,但在5那,y不一定是true;3同步于4,在6那,x不一定为true。z可能为0,1,2。

你可能感兴趣的:(c++11,c++)