在下面的示例中,我最终为两个线程调用了 pthread_join()
(在我打印总和之前)。尽管预计总和应为 0,但它会打印任何值。我知道如果我在创建第二个线程之前执行 pthread_join(id1,NULL)
那么它会工作正常(确实如此),但我不明白为什么当我最后为两个线程调用 join。
因为 sum 仅在两个线程必须完全执行完毕后才会打印。因此,在第一个线程执行后,它必须将 2000000 添加到变量 sum,第二个线程必须从总和中减去 2000000 sum 应该为 0
long long sum=0;
void* counting_thread(void* arg)
{
int offset = *(int*) arg;
for(int i=0;i<2000000;i++)
{
sum=sum+offset;
}
pthread_exit(NULL);
}
int main(void)
{
pthread_t id1;
int offset1 = 1;
pthread_create(&id1,NULL,counting_thread,&offset1);
pthread_t id2;
int offset2 = -1;
pthread_create(&id2,NULL,counting_thread,&offset2);
pthread_join(id1,NULL);
pthread_join(id2,NULL);
cout<<sum;
}
最佳答案
问题是 sum=sum+offset;
不是线程安全的。
这导致一些金额未被计算在内。
正如您指定的 C++,std::atomic<long long> sum;
会有所帮助,但您需要使用 +=
运算符,而不是线程不安全的 sum = sum + count;
sum += offset;
阻止更新的互斥体也会有所帮助。
如果没有这些更改,编译器可以生成代码,这
- 阅读
sum
在函数的开头,只有一个线程应用它的更改。 - 旧值为
sum
用于添加。 - 缓存中的状态不正确。
阅读优化
编译器可以在线程启动的时候合法的读取sum的值,加上n次offset,存储这个值。这意味着只有一个线程可以工作。
陈旧的值(value)
考虑以下汇编代码。
read sum
add offset to sum
store sum
thread1 thread2
1 read sum
2 add offset to sum read sum
3 store sum add offset to sum
4 read sum store sum
5 add offset to sum read sum
6 store sum add offset to sum
线程 2 的第 3 行将偏移量添加到旧值,这使得线程 1 的第 3 行丢失。
缓存中的状态不正确
在多线程系统中,进程的线程之间缓存可能不一致。
这意味着即使在 sum+=offset
之后已经执行,那么另一个核心/CPU 可能会看到预先更新的值。
这允许 CPU 运行得更快,因为它们可以忽略它们之间的数据共享。但是,当 2 个线程正在访问相同的数据时,需要考虑这一点。
std::atomic
/mutex 确保:-
- 该值是原子修改的(就好像
sum = sum + count
是不可分割的)。 - 值(value)在所有核心/CPU 中始终可见。
- 编译器不会重新排序 sum 的加载/存储,就好像它无法更改一样。
关于c++ - 线程同步问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33331864/