运行以下代码数百次,我希望打印的值始终是3
,但似乎只有大约75%的时间是3。这可能意味着我对C++中各种内存顺序的用途或原子操作的意义有误解。我的问题是:有没有办法保证下面程序的输出是可预测的
#include <atomic>
#include <iostream>
#include <thread>
#include <vector>
int main () {
std::atomic<int> cnt{0};
auto f = [&](int n) {cnt.store(n, std::memory_order_seq_cst);};
std::vector<std::thread> v;
for (int n = 1; n < 4; ++n)
v.emplace_back(f, n);
for (auto& t : v)
t.join();
std::cout << cnt.load() << std::endl;
return 0;
}
例如,以下是运行100次后的输出统计数据:
$ clang++ -std=c++20 -Wall foo.cpp -pthread && for i in {1..100}; do ./a.out; done | sort | uniq -c
2 1
21 2
77 3
您观察到的是与内存顺序正交的。
调度程序无法保证具有相同优先级的线程的执行顺序。即使CPU空闲并且线程被分配给不同的CPU,缓存未命中和锁争用也会使线程相对于其他CPU上的线程停滞。或者,如果CPU正忙于运行具有相同或更高优先级的其他线程,那么您的新线程将不得不等待,直到运行的线程耗尽其在内核中的时间片或块,无论之前发生什么,调度程序都很难预测。只有当系统有一个CPU时,新线程才会按预期的顺序运行,因为它们将在一个CPU上形成一个队列。
std::memory_order_relaxed
在这里就足够了,因为您不需要在存储到cnt
和存储/加载到其他非原子变量之间进行任何排序。std::atomic
总是原子的,std::memory_order
指定是否可以相对于std::atomic
变量的加载或存储对其他非原子变量的加载和存储进行重新排序。