程序执行语句的顺序是不确定的,甚至包括函数的参数的顺序。表达式a()+b()+c()在+号优先级的作用下,会被解释为(a()+b())+c(),但是c()的执行顺序是不定的,甚至可能先于a()和b()。
typedef enum memory_order{
memory_order_relaxed,
memory_order_consume,
memory_order_acquire,
memory_order_release,
memory_order_acq_rel,
memory_order_seq_cst
} memory_order;
//C++11起C++11定义了上述六种内存顺序,引入了四种内存模型。
memory_order_relaxed是一种宽松的内存顺序约束,即不对线程间指令执行顺序进行约束,只保证操作是原子的和单个线程内的执行顺序。
// Thread 1:
r1 = y.load(std::memory_order_relaxed); // A
x.store(r1, std::memory_order_relaxed); // B
// Thread 2:
r2 = x.load(std::memory_order_relaxed); // C
y.store(42, std::memory_order_relaxed); // Dstd::atomic<int> x(0);
std::atomic<int> y(0);
std::thread th1([&](){
int r1 = y.load(std::memory_order_relaxed); // A
x.store(r1, std::memory_order_relaxed); // B
std::cout<<"r1:"<<r1<<std::endl;
});
std::thread th2([&](){
int r2 = x.load(std::memory_order_relaxed); // C
y.store(42, std::memory_order_relaxed); // D
std::cout<<"r2:"<<r2<<std::endl;
});
th1.join();
th2.join();程序的输出大部分时r1和r2都为0,少数情况下r1=42和r2=0。这是由于D发生在了A之前,两个线程间对指令的执行顺序没有强制性约束,但由于一个线程内都是顺序执行的,因此r2发生于D之前,r2仍为0。
releaxed order的一个典型应用是程序计数器,shared_ptr中,因为程序计数器只需要是原子的,对顺序并不作要求。
#include <vector>
#include <iostream>
#include <thread>
#include <atomic>
std::atomic<int> cnt = {0};
void f()
{
for (int n = 0; n < 1000; ++n) {
cnt.fetch_add(1, std::memory_order_relaxed);
}
}
int main()
{
std::vector<std::thread> v;
for (int n = 0; n < 10; ++n) {
v.emplace_back(f);
}
for (auto& t : v) {
t.join();
}
std::cout << "Final counter value is " << cnt << '\n';
}当原子操作store在线程A中被标记为memory_order_release,相同的原子变量在另一个线程B中load时被标记为memory_order_acquire,那么所有线程A中的写操作(无论是否是原子的,以及relaxed原子操作类型)在线程B中均可见。
互斥锁(std::mutex或者基于原子变量的自旋锁-atomic spinlock),是release-acquire同步的一个例子。当一个锁由线程A释放release,又由另一个线程B获取acquire,那么所有在线程A释放锁之前的所有操作在线程B中都是可见的,但需要是相同的锁,即对相同对原子变量进行操作。
void producer()
{
std::string* 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"); // never fires
assert(data == 42); // never fires
}
int main()
{
std::thread t2(consumer);
std::thread t1(producer);
t1.join(); t2.join();
}以上assert会永远成立,因为release之前的写操作在另一个线程中始终是可见的。
compare_exchange_strongstd::atomic flag = {0};
flag.compare_exchange_strong(expected,val,std::memory_order_acq_rel);
flag和expected相比,如果相同,则用val替换flag。如果不相同,用val替换expected。
相比上述内存关系,Release-Consume是只对有关联关系的的原子变量的顺序产生约束,其他无关联依赖关系的原子变量不做限制。
典型的用例是读取很少修改的数据结构,如路由表,配置文件,防火墙配置文件等等。
std::atomic<std::string*> ptr;
int data;
void producer()
{
std::string* 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_consume)))
;
assert(*p2 == "Hello"); // never fires: *p2 carries dependency from ptr
assert(data == 42); // may or may not fire: data does not carry dependency from ptr
}
int main()
{
std::thread t1(producer);
std::thread t2(consumer);
t1.join(); t2.join();
}data可能并不等于42,但我运行了很多次并没有出现异常。
是C++内存模型的默认设置,有序一致性语义,保持最强的内存一致性。