C++ 一个有两个线程的程序,一个线程增加全局变量,另一个线程减少相同的变量,变量总是正确的吗?

C++ 一个有两个线程的程序,一个线程增加全局变量,另一个线程减少相同的变量,变量总是正确的吗?,c++,c,multithreading,locking,C++,C,Multithreading,Locking,我对这个问题感到困惑,它只是在我查看某人的C++代码时出现的 例如,在C++中: // Global var int g_var = 0; // thread 1 call Func1() forever: void Func1() { ++g_var; } // thread 2 call Func2() forever: void Func2() { --g_var; } 定义Func1调用时间times1,Func2调用时间times2 times1-times2是否总是=

我对这个问题感到困惑,它只是在我查看某人的
C++
代码时出现的

例如,在C++中:

// Global var
int g_var = 0;

// thread 1 call Func1() forever:
void Func1() {
  ++g_var;
}

// thread 2 call Func2() forever:
void Func2() {
  --g_var;
}
定义
Func1
调用时间
times1
Func2
调用时间
times2

  • times1-times2
    是否总是
    =g_var
  • 如果代码在
    C
    中,而不是在
    C++
    中,该怎么办
  • 如果
    g_var
    使用
    volatile
    装饰怎么办
  • 使用原子增加/原子减少是唯一正确的方法吗
我认为,它应该在
C++
中使用
violatile
,而不是像Windows中的
InterlockedIncrement
那样使用原子操作,因为汇编代码只是一行,一个
add
指令:

mov         eax,1
add         dword ptr [a],eax  

按照问题出现的顺序回答问题:

  • 否,您的程序具有未定义的行为。可能是这样的情况:线程1读取变量(要增加),被中断,然后线程2读取变量(要减少),减少变量并将其写回(因此与我们开始的值相比它是负一),然后线程1继续,增加其记忆的旧值并将其写回变量,与我们开始时的值相比,结果为正1

  • 这里的语言没有任何变化

  • volatile
    仅保证单个线程可能不会缓存变量的值,即
    ++g_var++g_var将读取变量,递增,写入,读取,递增,写入。优化器不会将其替换为
    g_var+=2
    。但是,阅读和写作之间的线索仍然会被打断,这是危险的部分

  • 这是一个需要原子操作的完美例子。在
    int
    上使用原子操作,或者使用
    std::atomic
    包装器,当您调用普通运算符时,该包装器会自动为您执行此操作,使代码更具可读性

    只需将
    int g_var
    替换为
    std::atomic g_var
    即可解决任何问题

      • 不,因为代码不是线程安全的
      • C代替C++没有关系。
      • volatile
        没有帮助,它阻止在寄存器或其他优化中“缓存”值,但不会使操作原子化
      • 对。或者,如果您可以选择使用锁定机制自己实现这些功能,则不需要

      int g_var=0应该是。为一个特定平台生成的汇编指令的可能重复并不重要。有些处理器需要单独的指令来读取和写入值。即使是一条指令也不一定意味着它是原子指令!请注意,不显式使用原子操作仍可能编译为原子的汇编指令(即,标准并不禁止它!)。但除非你明确使用原子学,否则你不能保证它能做到这一点。更糟糕的是:调试会很困难,因为您可能认为它可以工作,但这只是您在这台特定机器上的运气,但当移植到不同的体系结构时,它可能会崩溃!对于未定义的行为,通常是这样的:它可能在一个设置中工作,但不能保证在其他设置中工作。非常令人沮丧。
      violatile
      描述得非常形象;)。。。