C++20 Atomic 原子 内存模型(二)
原子是C++内存模型的基础
强/弱内存模型
1. 强内存模型
Leslie Lamport 定义了顺序一致性的概念
顺序一致性提供两个保证:
- 指令按源码的顺序执行
- 对所有线程的所有指令有全局的顺序
以上不仅仅作用于原子, 也影响着非原子变量
int main(int argc, char* argv[])
{
atomic<int> x(0);
atomic<int> y(0);
atomic<int> res1(0);
atomic<int> 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 << "res1=" << res1 << std::endl;
std::cout << "res2=" << res2 << std::endl;
}
多次运行:
至少有一个值为 1, 表明每个线程都保证了顺序一致性, 但存在交替执行的情况
2. 弱内存模型
使用弱内存模型这些指令会有更多意外的组合, 弱内存模型指令重排只保证单线程的相互依赖的的指令顺序正确
总结:
原子操作默认使用顺序一致性标志
Atomic Flag
std::atomic_flag
是原子布尔类型。不同于所有 std::atomic
的特化,它保证是免锁的。不同于 std::atomic<bool>
, 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<T>::is_always_lock_free) assert(std::atomic<T>().is_lock_free();
自旋锁
自旋锁是一种基本的锁,比如互斥锁。
与互斥锁相比,不会等到得到锁。它不断地要求锁进入关键部分。它节省了从用户空间到内核空间的昂贵的上下文切换,但它完全利用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<bool>
环境变量
std::vector<int> myShareWork;
std::mutex mutex_;
std::condition_variable condVar;
bool dataReady{false};
void waitingForWork()
{
std::cout << "working" << endl;
std::unique_lock<std::mutex> lk(mutex_);
condVar.wait(lk, [] { return dataReady; });
myShareWork[1] = 2;
std::cout << "Work done" << endl;
}
void setDataReady()
{
myShareWork = {1, 0, 3};
{
std::lock_guard<std::mutex> lk(mutex_);
dataReady = true;
}
std::cout << "Data prepared" << endl;
condVar.notify_one();
}
int main(int argc, char* argv[])
{
std::cout << std::endl;
std::jthread t1(waitingForWork);
std::jthread t2(setDataReady);
t1.join();
t2.join();
for (auto v : myShareWork)
{
std::cout << v << " ";
}
std::cout << "\n\n";
}
###使用std::atomic<bool>
实现环境变量
std::vector<int> myShareWork;
std::atomic<bool> dataReady(false);
void waitingForWork()
{
std::cout << "waiting" << endl;
while (!dataReady.load())
{
std::this_thread::sleep_for(std::chrono::milliseconds(5));
}
myShareWork[1] = 2;
std::cout << "done" << endl;
}
void setDataReady()
{
myShareWork = {1, 0, 3};
dataReady = true;
std::cout << "Data prepared" << std::endl;
}
int main()
{
std::cout << std::endl;
std::thread t1(waitingForWork);
std::thread t2(setDataReady);
t1.join();
t2.join();
for (auto v : myShareWork)
{
std::cout << v << " ";
}
std::cout << "\n\n";
}