全局变量的最终值,该变量正在通过两个线程进行增量

Final value of a global variable which is being incremented via two threads

本文关键字:两个 线程 变量 全局变量      更新时间:2023-10-16

我遇到了这个问题:

给定两个线程和一个全局变量var,两个线程运行 相同的代码(C/C 代码):

for(int i=0; i<20; i++)
{
  var++;
}

线程末端的var的可能值是多少 执行?

如果每个线程递增var"正确" - 我猜最大值将为40。

但是最低值呢?增量操作如何实现和实际完成?

注意:有目的的增量操作没有锁定(任何类型的)(当然,正确的方法是锁定它 - 问题是出于教育目的)。

如果 var不是原子类型,则行为是 nespined 。因此,关于可能最终值的范围的问题是不适合的。

如果 var是原子类型,则最终结果将是好像没有同时运行线程。

如果您不进行任何锁定,则var++不是原子,即写入和变量的读取可以被另一个线程中断。这意味着这两个线程都可能读取相同的VAR值并同时读取它。

最坏的情况这两个线程完全"同步",这些线程完全同时读写和编写VAR,因此var的最小终点为20。

更新:显然,这些数据竞赛中可能的结果无法保证,请参见下面的bathsheba答案,他提到的本文。

更新2 :我很好奇在现实世界中,我们是否会在下面看到值20。测试设置(详细信息:请参阅此GIST)

  • 全局int var。用longlong long测试,没有任何区别
  • 两个pthread S进行20'000倍var++
  • 可执行文件启动了1000次,最小的值

预期的结果将在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 |

看起来很有趣的东西:

  • 是的,它可以降至20'000以下,因此"不保证"主张不仅在理论上
  • 随着编译器的优化高于一定级别,行为看起来更可预测
  • 奇怪的是,在具有8个内核的EC2上,gcc -O2的最小值为40'000。