线程安全问题

时间:2010-03-01 16:02:14

标签: c++ thread-safety

这里的一个简单的情况, 如果我有三个线程,一个用于窗口应用,我希望它们退出 当窗口应用程序关闭时,如果我使用一个全局变量,它是线程安全的, 如果只有全局变量为真,则三个线程将退出,否则继续工作? 在这种情况下,波动是否有帮助? C ++编程。

5 个答案:

答案 0 :(得分:4)

理论上,volatile是不够的。有两个抽象层:

  • 源代码操作和实际操作码之间;
  • 介于核心/处理器之间以及其他核心/处理器看到的内容之间。

编译器可以自由地将数据缓存在寄存器中并重新排序读取和写入。通过使用volatile,您可以指示编译器生成操作码,这些操作码完全按照您在源代码中指定的顺序执行读取和写入。但这只处理第一层。管理处理器内核之间通信的硬件系统可能延迟并重新排序读写。

恰巧在x86硬件上,内核可以相当快地将写入传播到主内存,并且其他内核会自动通知内存已更改。因此volatile似乎已经足够了:它确保编译器不会使用寄存器来播放时髦的游戏,并且内存系统足以处理从那一点开始的事情。但请注意,在所有系统上都不是这样(我认为至少有一些Sparc系统可以延迟任意延迟的写入传播 - 可能需要几个小时),而且我在AMD的一本手册中读到了AMD明确保留的权利。传播在一些未来的处理器中不那么迅速地写入。

因此,无论何时访问全局变量(用于读取和写入),干净的解决方案都是在Unix上使用互斥锁(pthread_mutex_lock(),在Windows上使用EnterCriticalSection())。互斥原语包括一个称为内存屏障的特殊操作,类似于类固醇上的volatile(它对两个抽象层都充当volatile

答案 1 :(得分:3)

如果您只想从其他线程的共享变量中“读取”,那么在您描述的情况下就可以了。

是的, volatile 提示是必需的,否则编译器可能会“优化”变量。

等待线程完成(即join)也会很好:这样,应该发生的任何清理(应用程序)将有机会完成。

答案 2 :(得分:1)

不,由于memory visibility问题,这是有风险的。在多处理器上,在一个处理器上写入内存并不意味着不同的处理器会立即看到更改。此外,在不使用互斥锁的情况下,可能需要很长时间才能将更改传播到其他处理器。

答案 3 :(得分:0)

是的,这是一种常见的技术。

但是你也应该在主线程退出main()之前等待所有子线程退出 在大多数线程实现中,如果主线程退出main(),则会重复所有当前活动的子线程(请参阅线程文档以获取详细信息),而无需允许其堆栈正确展开。因此,RAII的所有好处都将丢失。

所以设置你的全局变量,然后等待(大多数线程系统有一个join方法允许你等待(对于处于非忙状态的线程)死掉)让所有的孩子在允许main()线程之前干净地退出退出。

答案 4 :(得分:0)

直到您更改变量的值以使线程退出为止,这是安全的。在那一点1)你需要同步访问,2)你需要做一些事情(抱歉,volatile是不够的),以确保新值正确传播到其他线程。

前一部分很简单。后者实际上更加困难 - 你几乎肯定需要使用某种库或操作系统提供的机制。