C++并发编程(二十四)
C++并发编程(二十四)
前言
除了基本的原子操做内存次序,标准库还提供了内存栅栏 std::atomic_thread_fence(),用以灵活改变内存次序。
内存栅栏可以插在原子操作之间,使得程序服从先行关系及同步关系,亦可插入普通操作与原子操作之间,同样可以令其服从内存次序。
一、内存栅栏
顾名思义,内存栅栏就是一种阻止内存乱序的机制,它可以灵活的添加在某些原子操作中间,使得此原子操作在多线程环境服从先行关系和同步关系。
#include <atomic>
#include <cassert>
#include <iostream>
#include <thread>
std::atomic<bool> x, y;
std::atomic<int> z;
//内存栅栏可以在多线程中引入先行关系和同步关系
void writeXThenY()
{
x.store(true, std::memory_order_relaxed);
//内存栅栏需要设置在两个需要服从先后次序的位置,因为多线程的同步点是栅栏本身
std::atomic_thread_fence(std::memory_order_release);
y.store(true, std::memory_order_relaxed);
}
void writeXThenYUseless()
{
//错误使用方式,无法使 x 和 y 服从先后次序
std::atomic_thread_fence(std::memory_order_release);
x.store(true, std::memory_order_relaxed);
y.store(true, std::memory_order_relaxed);
}
void readYThenX()
{
while (!y.load(std::memory_order_relaxed))
{
}
std::atomic_thread_fence(std::memory_order_acquire);
if (x.load(std::memory_order_relaxed))
{
++z;
}
}
auto main() -> int
{
x = false;
y = false;
z = 0;
std::thread a(writeXThenY);
std::thread b(readYThenX);
a.join();
b.join();
assert(z.load() != 0);
std::cout << "learnThread_70.cpp" << std::endl;
return 0;
}
二、通过原子操作令普通变量服从内存次序
内存栅栏插入到普通操作和原子操作之间,也可以约束内存次序,达到通过确立同步关系,强制先行关系的目的。当然,将原子操作的内存次序直接设为释放捕获对 memory_order_release 和 memory_order_acquire 或 memory_order_consume 可达到同样目的。
#include <atomic>
#include <cassert>
#include <iostream>
#include <thread>
bool x = false;
std::atomic<bool> y;
std::atomic<int> z;
void writeXThenY()
{
//因为 y 的原子操作的同步,使得这里的 X 写入必然先行于 readYThenX() 中的读取
x = true;
std::atomic_thread_fence(std::memory_order_release);
//y 必须是原子操作,否则会引发数据竞争
y.store(true, std::memory_order_relaxed);
}
void readYThenX()
{
while (!y.load(std::memory_order_relaxed))
{
}
//通过内存栅栏同步 y 的原子操作, 强制 x 读写的先行关系
std::atomic_thread_fence(std::memory_order_acquire);
//由于 y 的同步,确立了 x 读写的先行关系,免除了数据竞争
if (x)
{
++z;
}
}
auto main() -> int
{
x = false;
y = false;
z = 0;
std::thread a(writeXThenY);
std::thread b(readYThenX);
a.join();
b.join();
assert(z.load() != 0);
return 0;
}
总结
如果对前文的原子操作内存次序,及先行关系,同步关系等都掌握的话,本文则并不难理解。原子操作部分,介绍就到这里了。