为什么我的线程计数器并不总是完成?

问题描述:

我认为我的程序有一个错误,因为有时当我运行我的程序时,它会输出一个比30000小的数字,比如29999.但有时它会正确运行并进入30000.我的问题是如何解决这个问题,为什么它发生了吗?为什么我的线程计数器并不总是完成?

#include <iostream> 
#include <thread> 

using namespace std; 

int counter; 
int i; 

void increment() 
{ 
counter++; 
} 

int main() 
{ 
counter = 0; 

cout << "The value in counter is : " << counter << endl; 
thread tarr[30000]; 

    for (i = 0; i < 30000; i++) 
    { 
     tarr[i] = thread(increment); 
    } 

    for (i = 0; i < 30000; i++) 
    { 
     tarr[i].join(); //main thread waits for tarr to finish 
    } 

cout << "After running 30,000 threads "; 
cout << "the value in counter is : " << counter << endl; 
return 0; 
} 
+0

该增量是完全无人防守的* *和竞争条件的食谱。而且你应该感谢几乎所有线程完成的速度,因为30000个线程比大多数桌面操作系统能够处理的大约多27000个,而不需要全身心投入,并且磨合到一个上下文切换的暂停状态。 – WhozCraig

+0

@aghilpro线程在最后两个'cout'转储之前已经加入。你有什么可能的好处,你猜测一个'睡眠'会做?\ – WhozCraig

+0

一个良好的睡眠后,事情往往会更好。你醒来时已经清醒过来,准备再次看看代码,并且......哦,“睡眠”。是的,这不会有所帮助。 – user4581301

的问题是,counter++can be broken down into three operations

  1. 负荷初始值寄存器
  2. 增加值
  3. 存储新的值返回到存储器

单个线程可能会执行前两个步骤,然后将控制权交给另一个线程来执行sa我。什么这可能意味着是:

  1. 线程一读counter5
  2. 线程一个增加它的内部副本6
  3. 线程两次读取counter5
  4. 线程两个增量其内部副本6
  5. 线程2写回6counter
  6. 线程一个写回6counter

你应该让counterstd::atomic,或者用std::mutex保护它:

std::atomic<int> counter; 
+0

尽管“三个操作中断”部分听起来很合理,但根据C++实际上是未定义的行为。这很重要,因为这里提供的模型表明可能结果的范围比实际情况要小。特别是,这个答案表明计数器必须至少加1个线程,所以结果必须在1到30.000之间。一个合理的优化编译器可能会删除所有不安全的赋值给'counter',产生0的同样有效的结果。不过,这个答案的最终结论是正确的。 – MSalters