首页 > 解决方案 > 如何理解两个线程递增和打印一个全局变量的结果?

问题描述

在我的主线程中,我有两个线程运行以下函数:

int i = 0;    

void foo()
{
   ++i;
   printf("%d", i);
}

我运行了 10000 次,得到了 3 个不同的结果:{1 2}、{1 1}、{2 1}

前两个我明白。问题是结果如何是 {2 1} 以及为什么 {2 2} 根本不出现。

谢谢!

标签: concurrency

解决方案


要了解为什么任何特定的未定义行为实例碰巧在特定机器上以特定方式执行,您通常必须阅读编译器生成的汇编代码。 是 x86-64 上的 gcc 10.2 产生的内容。它大致执行以下操作:

  1. 从内存加载i到寄存器
  2. 增加寄存器
  3. 将寄存器存储回i
  4. 将寄存器的内容传递给printf.

所以想象一下线程碰巧按以下顺序执行这些步骤:

Thread A                  Thread B
--------                  --------
Step 1
Step 2
Step 3
                          Step 1
                          Step 2
                          Step 3
                          Step 4
Step 4

然后你可以清楚地看到输出将是2 1.

至于为什么2 2永远不会发生,请注意要打印的值是在第 1 步加载的。两个线程之一必须先于另一个线程执行第 1 步,或者同时执行。因此,其中至少有一个将0在第 1 步加载,因此将打印1. 即使另一个线程碰巧i在第一个线程打印它的值之前更新了内存中的值,它也不会有任何区别,因为要打印的值已经在第一个线程的寄存器中。

(当然,其他一些编译器完全有可能生成i从内存重新加载的代码,在这种情况下2 2可能会发生。对于这个特定的生成代码,情况并非如此。)


推荐阅读