c - 如果 volatile 对线程没有用,为什么原子操作需要指向 volatile 数据的指针?

标签 c atomic volatile

我从很多来源读到 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/

相关文章:

c - 递归仅在使用断点或 getchar 时有效

c++ - 是什么让 C++ atomic 成为 atomic

java - 即使两个线程不同时读写,我是否也需要同步?

c - C volatile变量和高速缓存

java - volatile 变量未读取其更新值

c++ - 嵌套 IF 语句与 IF-ELSE

c - 结构末尾的空数组是 C 标准吗?

c - 为什么将字符串的 scanf 保存到 str 而将 int 发送到 i 的指针?

cuda - CUDA 中的原子 Saxpy

linux - 为什么在 Linux 中 spin_lock 和 spin_unlock 之间的中断被禁用?