Concurrency 如何理解通过两个线程递增和打印全局变量的结果?

Concurrency 如何理解通过两个线程递增和打印全局变量的结果?,concurrency,Concurrency,我主要有两个线程运行以下函数: int i = 0; void foo() { ++i; printf("%d", i); } 我运行了10000次,得到了3个不同的结果: {12},{11},{21} 前两个我明白。问题是结果如何是{21},以及为什么{22}根本不出现 谢谢大家! 要理解为什么未定义行为的任何特定实例在特定机器上以特定方式执行,通常必须阅读编译器生成的汇编代码。是x86-64上的gcc 10.2产生的。它大致做到了以下几点: 将i从

我主要有两个线程运行以下函数:

int i = 0;    

void foo()
{
   ++i;
   printf("%d", i);
}
我运行了10000次,得到了3个不同的结果: {12},{11},{21}

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


谢谢大家!

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

  • i
    从内存加载到寄存器中
  • 递增寄存器
  • 将寄存器存储回
    i
  • 将寄存器的内容传递到
    printf
  • 因此,假设线程按照以下顺序执行这些步骤:

    Thread A                  Thread B
    --------                  --------
    Step 1
    Step 2
    Step 3
                              Step 1
                              Step 2
                              Step 3
                              Step 4
    Step 4
    
    然后您可以清楚地看到输出将是
    21

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


    (当然,完全有可能其他编译器会在从内存重新加载
    i
    的情况下生成代码,在这种情况下,可能会发生
    2
    。对于这个特定生成的代码,情况刚好不是这样。)

    这是一场数据竞赛,因为您可以从两个不同的线程访问非原子对象,而无需锁定。C中的数据竞争会触发未定义的行为,并可能导致任何输出、崩溃或其他任何结果。因此在某种意义上,理解代码为什么有任何特定行为是没有意义的;你应该做的是修正它以获得你想要的行为。谢谢你的回复。我知道这是一场比赛。我的目标不是要有一个正确工作的代码,而是要理解某些结果的原因非常感谢您的详细答复!我稍微修改了我的代码,使我变得易变。现在传递给printf的i的值将被更新,所以你给出的例子的结果将是2。但我有时还是会得到21分。你能解释一下这个例子吗?@Miriam:那么,在上面添加一个步骤3.5,将
    i
    从内存重新加载到寄存器(或堆栈)中。然后假设线程B在A执行步骤3.5和4时完成了这两个步骤之间的所有工作。请记住,C是通过值传递的,因此为了将
    i
    作为参数传递给
    printf
    ,必须复制它。如果线程B在复制和打印之间运行,您将得到
    2 1