我想实现以下内容
producer
(可以改变共享变量a
),consumer
(等待共享变量改变)问题 - 我用
int a;
和 std::atomic<int> a;
运行我的代码,但在 100% 的情况下,我在我的 PC 上得到相同的结果。
calculate_val
process 1
如何查看有和没有原子的代码变化?
using namespace std;
atomic<int> a; // I was replacing with int a; and always have the same result
int val;
int calculate_val() {
cout << "calculate_val " << endl;
return 1;
}
void process(int val) {
cout << "process " << val << endl;
}
void threadProducer() {
val = calculate_val();
a = 1;
}
void threadConsumer() {
while (!a) {
}
process(val);
}
int main() {
thread t2(threadConsumer);
thread t1(threadProducer);
t2.join(); // was trying to change order
t1.join();
return 0;
}
你的场景太简单了。在具有强有序内存语义的架构上(例如 x86 和后代,如果你提到“我的电脑”,它可能就是你正在使用的),关闭编译器优化,只要编译器不重新排序
threadProducer
的 a = 1;
在函数调用之前(它没有理由这样做),不会为 a
测试缓存 while (!a)
的值,并且不会消除 while(a){}
循环,原子不会有什么不同。
如果没有原子,您现有的代码可能会失败:
a
和 val
的写入可以在 threadConsumer
中以任何顺序可见,并且它们可能需要任意长的时间才能变得可见)。我怀疑 iostream
库的内置锁定,以及它包装的 C stdio
可能会触发缓存刷新,所以你的 I/O 试图 observe 比赛实际上是 changing 行为.a
中缓存 threadConsumer
的值以提高效率(非volatile
,非atomic
值可以被缓存以节省开销,因为假设它们只能从访问一个线程,加载线程没有改变它)。while (a) {}
循环,因为非原子 a
如果按照 #2 中的建议进行缓存,则可能会创建一个无限循环。a = 1;
之前重新排序 val = calculate_val();
(因为这两行都不依赖于另一行计算的任何值,优化编译器可以选择以任一顺序执行它们)。实际上,鉴于上述 I/O 层中的锁定,编译器 不会 交换这些行,但如果 I/O 被删除,它 可以我不打算给出可能出错的例子,因为粗略的搜索会找到各种关于何时以及为什么使用原子的例子。重要的是要知道,即使在您 thought 没有它们就安全的简单场景中,事实并非如此。即使开启优化也可能导致您的非原子代码
a
停止在 x86-64 机器上运行,而 x86-64 已经在简单模式下进行线程/原子操作。