C,C++非同步线程返回奇怪的结果

Mor*_*lus 5 c c++ multithreading thread-safety

好吧,我有一个关于线程的问题.

有两个未同步的线程同时运行并使用全局资源"int num"1st:

    void Thread()
{ 
    int i;
    for ( i=0 ; i < 100000000; i++ )
    {
        num++;
        num--;
    }
}
Run Code Online (Sandbox Code Playgroud)

第二:

    void Thread2()
{ 
    int j;
    for ( j=0 ; j < 100000000; j++ )
    {
        num++;
        num--;      
    }
}
Run Code Online (Sandbox Code Playgroud)

问题陈述:程序结束时变量"num"的可能值是什么.现在我会说0将是程序结束时num的值,但是,尝试运行此代码,你会发现结果是随机的,我不明白为什么?

完整代码:

 #include <windows.h>
    #include <process.h>
    #include <stdio.h>

    int static num=0;

   void Thread()
    { 
        int i;
        for ( i=0 ; i < 100000000; i++ )
        {
            num++;
            num--;
        }
    }

   void Thread2()
    { 
        int j;
        for ( j=0 ; j < 100000000; j++ )
        {
            num++;
            num--;      
        }
    }

    int main()
    {
        long handle,handle2,code,code2;
        handle=_beginthread( Thread, 0, NULL );
        handle2=_beginthread( Thread2, 0, NULL );

        while( (GetExitCodeThread(handle,&code)||GetExitCodeThread(handle2,&code2))!=0 );

        TerminateThread(handle, code );
        TerminateThread(handle2, code2 );

        printf("%d ",num);
        system("pause"); 
    }
Run Code Online (Sandbox Code Playgroud)

Tho*_*mas 21

num++并且num--不必是原子操作.举num++个例子,这可能是这样实现的:

int tmp = num;
tmp = tmp + 1;
num = tmp;
Run Code Online (Sandbox Code Playgroud)

tmpCPU寄存器中保存的位置.

现在让我们说num == 0,两个线程都尝试执行num++,并且操作交错,如下所示:

Thread A        Thread B
int tmp = num;
tmp = tmp + 1;
                int tmp = num;
                tmp = tmp + 1;
num = tmp;
                num = tmp;
Run Code Online (Sandbox Code Playgroud)

num == 1即使它应该增加两次,结果的结果也将是结果.这里丢失了一个增量; 以同样的方式,减量也可能会丢失.

在病态情况下,一个线程的所有增量都可能丢失,导致num == -100000000,或者一个线程的所有减少都可能丢失,从而导致num == +100000000.甚至可能有更多的极端情景潜伏在那里.

然后还有其他业务正在进行,因为num未被声明为volatile.因此,两个线程都会假设值num不会改变,除非它们是改变它的值.for如果感觉如此倾向,这允许编译器优化掉整个循环!

  • 当然不是.我暗示它确实存在吗?我只是说没有`volatile`会使程序比现在更加错误. (7认同)
  • `volatile`确实*NOT*在C和C++中具有原子访问语义. (2认同)
  • @Axel的论点是修复问题需要使用`volatile`*以及*进行适当的同步(假设我们在过程中而不是在结束时关心`i`的值,并假设我们关心有一个实际执行两个循环描述的工作的程序).由于优化,正确地同步并省去`volatile`可能仍然不好.(当然,除非对同步原语的调用的存在干扰了那些优化 - 实际上我想它会这样做.) (2认同)
  • @Karl:同步原语必须暗示编译器障碍,如果它们是正确的,那么`volatile`是不必要的. (2认同)