我遇到了这个问题:
给定两个线程和一个全局变量
var
,两个线程运行相同的代码(C / C ++代码):for(int i=0; i<20; i++) { var++; }
在线程执行结束时
var
的可能值是什么?
如果每个线程“正确”增加var
- 我猜最大值将是40。
但最低价值呢?如何实施增量操作并实际完成?
注意:在有意增加操作中没有锁定(任何类型)(当然,正确的方法是锁定它 - 问题是出于教育目的)。
如果var
不是原子类型,则行为未定义。因此,关于可能的最终值范围的问题是不适当的。
如果var
是原子类型,那么最终结果就好像线程没有同时运行一样。
如果你不做任何锁定,那么var++
不是原子的,即变量的写入和读取可以被另一个线程中断。这意味着两个线程都可以读取相同的var值,并同时增加它。
最坏的情况是两个线程完全“同步”,即在完全相同的时间读取和写入var,因此var
的最小结束值将为20。
更新:显然,在这些数据竞赛中无法保证可能的结果,请参阅下面的Bathsheba答案和他所提到的this article。
更新2:我很好奇,如果在现实世界中我们会看到低于20的值。测试设置(详情:请参阅this gist)
int var
。用long
和long long
测试,没有任何区别pthread
s做20'000倍var++
预期结果将在20,000到40'000之间:
| Computer | clang | clang -O3 | gcc | gcc -O0 | gcc -O2 |
|-----------------------------------------------|-------|-----------|-------|---------|---------|
| Linux, ThinkPad, x86_64, Intel I7, 4 cores | 19402 | 20000 | 18760 | 16913 | 20000 |
| Linux, Raspberry 3, ARMv7, 4 cores | 19587 | 20000 | 19569 | 19904 | 20000 |
| OSX, Intel i5, 4 cores | 17609 | 20000 | 17206 | 18049 | 20000 |
| Linux, EC2 t2.2xlarge, Intel Xeon E5, 8 cores | 19707 | 20000 | 19744 | 19881 | 40000 |
看起来很有趣:
gcc -O2
的最小值为40'000。