C++20 Atomic 原子 内存模型(二)
原子是C++内存模型的基础
强/弱内存模型
1. 强内存模型
Leslie Lamport 定义了顺序一致性的概念
顺序一致性提供两个保证:
- 指令按源码的顺序执行
- 对所有线程的所有指令有全局的顺序
以上不仅仅作用于原子, 也影响着非原子变量
int main(int argc, char* argv[])
{
atomic x(0);
atomic y(0);
atomic res1(0);
atomic res2(0);
std::jthread t1([&]()
{
x.store(1);
res1 = y.load();
});
std::jthread t2([&]()
{
y.store(1);
res2 = x.load();
});
//*this线程等待其他线程执行完成
t1.join();
t2.join();
std::cout
多次运行:
至少有一个值为 1, 表明每个线程都保证了顺序一致性, 但存在交替执行的情况
2. 弱内存模型
使用弱内存模型这些指令会有更多意外的组合, 弱内存模型指令重排只保证单线程的相互依赖的的指令顺序正确
总结:
原子操作默认使用顺序一致性标志
Atomic Flag
std::atomic_flag
是原子布尔类型。不同于所有 std::atomic
的特化,它保证是免锁的。不同于 std::atomic
, std::atomic_flag
不提供加载或存储操作。
ATOMIC_FLAG_INIT
:定义能以语句 std::atomic_flag v = ATOMIC_FLAG_INIT
; 用于初始化 std::atomic_flag
以清除(置 false
)状态的初始化器。它能否用于其他初始化语境中是未指定的。
无锁原子
“std::is_always_lock_free`
在不同的平台上原子的实现可能不同, 可以使用std::is_always_lock_free
来检查院子是如何实现的
if (std::atomic
自旋锁
自旋锁是一种基本的锁,比如互斥锁。
与互斥锁相比,不会等到得到锁。它不断地要求锁进入关键部分。它节省了从用户空间到内核空间的昂贵的上下文切换,但它完全利用CPU并浪费CPU周期。如果线程通常被阻塞短时间,自旋锁是相当有效的。
通常一个锁使用自旋锁和互斥锁的组合。锁首先在一个有限的时间段内使用自旋锁。如果没有成功,则使线程处于等待状态。
class Spinlock
{
std::atomic_flag flag = ATOMIC_FLAG_INIT;
public:
void lock()
{
while (flag.test_and_set());
}
void unlock()
{
flag.clear();
}
};
Spinlock spin;
void workOnResource()
{
spin.lock();
spin.unlock();
}
int main()
{
std::thread t(workOnResource);
std::thread t2(workOnResource);
t.join();
t2.join();
}
condition variable 与 std::atomic
环境变量
std::vector myShareWork;
std::mutex mutex_;
std::condition_variable condVar;
bool dataReady{false};
void waitingForWork()
{
std::cout lk(mutex_);
condVar.wait(lk, [] { return dataReady; });
myShareWork[1] = 2;
std::cout lk(mutex_);
dataReady = true;
}
std::cout
###使用std::atomic
实现环境变量
std::vector myShareWork;
std::atomic dataReady(false);
void waitingForWork()
{
std::cout