没有同步的C++多线程

Sam*_*Sam 1 c++ multithreading

C++:我有2个线程,每个线程通过做n ++(n是全局变量)递增'n'假设我没有使用任何线程同步,我的要求是打印0,1,2,.... 10.

int n = 0 //global variable
Run Code Online (Sandbox Code Playgroud)

线程1:

n++;
printf("%d", n);
Run Code Online (Sandbox Code Playgroud)

线程2:

n++;
printf("%d", n);
Run Code Online (Sandbox Code Playgroud)

在没有线程同步的情况下执行程序会有任何问题,或者它是否满足我的要求(1,2,3 ... 10).

Jon*_*ely 7

C++标准说这个程序有不确定的行为,任何事情都可能发生.任何东西,包括重复打印相同的数字,不打印任何东西,崩溃程序或擦拭硬盘.

1.10 [intro.multithread]

如果其中一个修改内存位置(1.7)而另一个读取或修改相同的内存位置,则两个表达式评估会发生冲突.
...
程序的执行包含数据竞争,如果它包含两个可能同时发生冲突的动作,其中至少有一个不是原子的,并且除了下面描述的信号处理程序的特殊情况之外,它们都不会发生在另一个之前.任何此类数据争用都会导致未定义的行为.

您应该使用std::atomic<int>由多个线程同时修改的变量,或者与互斥锁同步,以避免由于数据争用导致的未定义行为.

对于您的用例,您需要确保每次更新变量时都要打印其值,即增量和打印必须以原子方式进行.简单地以原子方式更新整数是不够的,你必须确保打印该增量的结果,而不是稍后打印变量的值(当另一个线程可能已经改变它时,如Rotem的答案所示).

这可以这样做:

std::atomic<int> n{0}; // global variable
Run Code Online (Sandbox Code Playgroud)

线程1:

printf("%d", ++n);
Run Code Online (Sandbox Code Playgroud)

线程2:

printf("%d", ++n);
Run Code Online (Sandbox Code Playgroud)

现在每次线程递增变量时,该增量的结果将直接传递给printf.

这假设写入stdout是线程安全的并按顺序发生,以避免该假设使用互斥锁创建更新变量并打印它的关键部分,防止其他线程在互斥锁被锁定时递增变量或打印:

int n = 0 //global variable
std::mutex mtx; // global mutex
Run Code Online (Sandbox Code Playgroud)

线程1:

{
  std::lock_guard<std::mutex> lock(mtx);
  ++n;
  printf("%d", n);
}
Run Code Online (Sandbox Code Playgroud)

线程2:

{
  std::lock_guard<std::mutex> lock(mtx);
  ++n;
  printf("%d", n);
}
Run Code Online (Sandbox Code Playgroud)

NB仍然无法保证两个线程将交替递增和打印,完全有可能第一个线程将执行十个增量并打印整个输出,而第二个线程没有机会运行.

  • @MDavies即使使用原子增量和打印,您也可以获得交错"线程A增量;线程B增量;线程A打印;线程B打印;". (2认同)