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";
}