一个简单的情况,如果我有三个线程,其中一个用于窗口应用程序,并且我希望当窗口应用程序关闭时它们都停止运行,那么如果我使用一个全局变量,那么只有当全局变量为真时,这三个线程才会停止,否则它们将继续工作,这种情况下是否是线程安全的?
在C++编程中,volatile关键字是否有帮助?
一个简单的情况,如果我有三个线程,其中一个用于窗口应用程序,并且我希望当窗口应用程序关闭时它们都停止运行,那么如果我使用一个全局变量,那么只有当全局变量为真时,这三个线程才会停止,否则它们将继续工作,这种情况下是否是线程安全的?
在C++编程中,volatile关键字是否有帮助?
volatile
是不够的。存在两个抽象层:
volatile
可以指示编译器按照源代码中指定的顺序执行读取和写入操作生成操作码。但这只处理了第一层。管理处理器核心之间通信的硬件系统也可能延迟和重新排序读取和写入。volatile
看起来是足够的:它确保编译器不会在寄存器中进行奇怪的操作,并且内存系统足够友好,可以从那一点开始处理事情。请注意,这在所有系统上都不是真的(我认为至少有一些 Sparc 系统可以延迟任意时间的写入传播,可能是几个小时),我曾在 AMD 的手册中读到过,AMD 明确保留了在某些未来的处理器中稍后传播写入的权利。pthread_mutex_lock()
,Windows 上的 EnterCriticalSection()
)。互斥原语包括一种特殊操作,称为“内存屏障”,它类似于增强版的 volatile
(它对两个抽象层都起作用)。如果你只想从其他线程的共享变量中“读取”,那么在你描述的情况下是可以的。
是的,需要使用volatile提示,否则编译器可能会“优化掉”该变量。
等待线程完成(即join
)也很好:这样,应用程序应进行的任何清理都有机会完成。
volatile
提示...嵌入式软件背景有所帮助。 - jldupont不行,因为存在内存可见性问题,这是有风险的。在多处理器上,向一个处理器的内存写入并不意味着另一个处理器会立即看到该更改。此外,如果不使用互斥锁,可能需要相当长的时间才能将更改传播到其他处理器。
是的,这是一种常见的技术。
但在主线程退出main()之前,您还应该等待所有子线程退出。
在大多数线程实现中,如果主线程退出main(),则所有当前活动的子线程都会被重复执行(请参阅您的线程文档以了解详细信息),而不允许它们的堆栈正确地展开。因此,RAII的所有好处都将丧失。
因此,设置全局变量,然后等待(大多数线程系统都有join方法,允许您等待(处于非忙碌状态的线程)安全退出)所有子线程干净地退出,然后才允许主线程退出。
只要您更改变量的值以使线程退出,它就是安全的。在此时,您需要1)同步访问,并且2)需要做一些(抱歉,易失性不足够)来确保新值被正确地传播到其他线程。
前者很容易。 后者则更加困难 - 到了几乎肯定需要使用某种库或操作系统提供的机制的程度。
volatile
最初是为了与内存映射 I/O 设备和信号处理程序(信号处理程序是异步的,但发生在同一个核心上)进行交互而设计的。有很多代码使用volatile
进行操作,并且会受到带屏障的volatile
的惩罚。volatile
不是为了成为屏障而设计的。请注意,其他语言的做法不同。Java 的volatile
包括内存屏障(甚至确保原子读写,即使是像long
和double
这样的“大”值类型)。 - Thomas Pornin