为什么我的线程计数器并不总是完成?
问题描述:
我认为我的程序有一个错误,因为有时当我运行我的程序时,它会输出一个比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;
}
答
的问题是,counter++
can be broken down into three operations:
- 负荷初始值寄存器
- 增加值
- 存储新的值返回到存储器
单个线程可能会执行前两个步骤,然后将控制权交给另一个线程来执行sa我。什么这可能意味着是:
- 线程一读
counter
为5
- 线程一个增加它的内部副本
6
- 线程两次读取
counter
为5
- 线程两个增量其内部副本
6
- 线程2写回
6
至counter
- 线程一个写回
6
到counter
你应该让counter
std::atomic
,或者用std::mutex
保护它:
std::atomic<int> counter;
+0
尽管“三个操作中断”部分听起来很合理,但根据C++实际上是未定义的行为。这很重要,因为这里提供的模型表明可能结果的范围比实际情况要小。特别是,这个答案表明计数器必须至少加1个线程,所以结果必须在1到30.000之间。一个合理的优化编译器可能会删除所有不安全的赋值给'counter',产生0的同样有效的结果。不过,这个答案的最终结论是正确的。 – MSalters
该增量是完全无人防守的* *和竞争条件的食谱。而且你应该感谢几乎所有线程完成的速度,因为30000个线程比大多数桌面操作系统能够处理的大约多27000个,而不需要全身心投入,并且磨合到一个上下文切换的暂停状态。 – WhozCraig
@aghilpro线程在最后两个'cout'转储之前已经加入。你有什么可能的好处,你猜测一个'睡眠'会做?\ – WhozCraig
一个良好的睡眠后,事情往往会更好。你醒来时已经清醒过来,准备再次看看代码,并且......哦,“睡眠”。是的,这不会有所帮助。 – user4581301