内存模型的基础知识

从并发的角度来看,内存模型要解答两个问题:

  • 什么是内存位置?
  • 如果两个线程访问相同的内存位置,会发生什么?

内存位置是什么?

引用cppreference.com中对内存位置的定义:

  • 标量对象(算术类型、指针类型、枚举类型或std::nullptr_t),
  • 或非零长度的连续序列。

下面是内存位置的例子:

  1. struct S {
  2. char a; // memory location #1
  3. int b : 5; // memory location #2
  4. int c : 11, // memory location #2 (continued)
  5. : 0,
  6. d : 8; // memory location #3
  7. int e; // memory location #4
  8. double f; // memory location #5
  9. std::string g; // several memory locations
  10. };

首先,对象obj由七个子对象组成,其中b、c两个位字段共享内存位置。

观察上述结构体定义,可以得到如下结论:

  • 每个变量都是一个对象。
  • 标量类型占用一个内存位置。
  • 相邻的位字段(b和c)具有相同的内存位置。
  • 变量至少占用一个内存位置。

那么,到了多线程的关键部分。

两个线程访问相同的内存位置,会发生什么呢?

如果两个线程访问相同的内存位置(相邻位字段共享内存位置),并且至少有一个线程想要修改它,那么程序就会产生数据竞争,除非:

  1. 修改操作为原子操作。
  2. 访问按照某种先行(happens-before)顺序进行。

第二种情况非常有趣,同步语义(如互斥锁)可以建立了先行关系。这些先行关系基于原子建立,当然也适用于非原子操作。内存序(memory-ordering)是内存模型的关键部分,其定义了先行关系的细节。

对内存模型有了初步的认识后,再来看看C++内存模型中定义的“编程协议”。