我从很多来源读到 the volatile
keyword is not helpful in multithreaded scenarios .然而,这种断言不断受到接受 volatile
指针的原子操作函数的挑战。
例如,在 Mac OS X 上,我们有 OSAtomic
函数系列:
SInt32 OSIncrementAtomic(volatile SInt32 *address);
SInt32 OSDrecrementAtomic(volatile SInt32 *address);
SInt32 OSAddAtomic(SInt32 amount, volatile SInt32 *address);
// ...
Windows 上的 volatile
关键字似乎也有类似的用法,用于 Interlocked
操作:
LONG __cdecl InterlockedIncrement(__inout LONG volatile *Addend);
LONG __cdecl InterlockedDecrement(__inout LONG volatile *Addend);
似乎在 C++11 中,原子类型具有带有 volatile
修饰符的方法,这一定意味着 volatile
关键字与原子性。
那么,我错过了什么?为什么 OS 供应商和标准库设计者坚持将 volatile
关键字用于线程目的,如果它没有用的话?
最佳答案
Volatile 对于多线程的共享访问并非毫无用处——只是它不一定足够:
- 它不一定提供可能需要的内存屏障语义;
- 它不提供原子访问保证(例如,如果 volatile 对象大于平台的 native 内存字长)
此外,您还应该注意,示例中 API 的指针参数上的 volatile
限定符实际上只是增加了 API 接收指向 volatile
的指针的能力> 没有提示的对象 - 它不需要指针指向实际的 volatile
对象。该标准允许将非限定指针自动转换为限定指针。标准中未提供自动转向其他方式(指向非限定的限定指针)(编译器通常允许这样做,但会发出警告)。
例如,如果 InterlockedIncrement()
的原型(prototype)为:
LONG __cdecl InterlockedIncrement(__inout LONG *Addend); // not `volatile*`
API 仍然可以实现以在内部正常工作。但是,如果用户有一个他想要传递给 API 的可变对象,则需要强制转换以防止编译器抛出警告。
由于(无论是否必要),这些 API 通常与 volatile
限定对象一起使用,将 volatile
限定符添加到指针参数可防止在API 被使用,并且当 API 与指向非 volatile 对象的指针一起使用时不会造成任何损害。
关于c - 如果 volatile 对线程没有用,为什么原子操作需要指向 volatile 数据的指针?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6397662/