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

| 我从许多来源都读到,
volatile
关键字在多线程方案中无济于事。但是,这种断言一直受到接受“ 0”指针的原子操作函数的挑战。 例如,在Mac OS X上,我们具有
OSAtomic
函数家族:
SInt32 OSIncrementAtomic(volatile SInt32 *address);
SInt32 OSDrecrementAtomic(volatile SInt32 *address);
SInt32 OSAddAtomic(SInt32 amount, volatile SInt32 *address);
// ...
似乎Windows上的ѭ5seems操作也有类似的用法a0ѭ关键字:
LONG __cdecl InterlockedIncrement(__inout LONG volatile *Addend);
LONG __cdecl InterlockedDecrement(__inout LONG volatile *Addend);
似乎在C ++ 11中,原子类型的方法带有
volatile
修饰符,这一定程度上意味着
volatile
关键字与原子性具有某种关系。 那么,我想念什么?如果无用的话,为什么操作系统供应商和标准库设计师坚持使用
volatile
关键字进行线程化?     
已邀请:
        对于多个线程的共享访问,Volatile并不是没有用的-只是这并不一定足够: 它不一定提供可能需要的内存屏障语义; 它不提供原子访问的保证(例如,如果易失对象大于平台的本机内存字长) 另外,您还应该注意,示例中指向API的指针参数上的“ 0”限定符实际上仅确实增加了API接收指向“ 0”对象的指针而不会产生抱怨的功能-它不需要将指针指向实际
volatile
个对象。该标准允许将不合格的指针自动转换为合格的指针。标准中未提供自动进行其他操作的方法(合格的指针指向不合格的指针)(编译器通常允许这样做,但会发出警告)。 例如,如果将
InterlockedIncrement()
原型化为:
LONG __cdecl InterlockedIncrement(__inout LONG *Addend);  // not `volatile*`
该API仍可以实现以在内部正常工作。但是,如果用户有一个易失的对象要传递给API,则需要强制转换以防止编译器抛出警告。 既然(必要或不需要),这些API通常与
volatile
限定对象一起使用,因此将
volatile
限定符添加到指针参数可防止在使用API​​时生成无用的诊断,并且在将API与指向a的指针一起使用时无害。非易失性对象。     
        突然想到我只是误解了“ 17”的含义。就像
const*
表示不应更改指针,
volatile*
则表示不应将指针缓存在寄存器中。这是一个可以自由添加的附加约束:可以将
char*
转换为
const char*
,也可以将
int*
转换为
volatile int*
。 因此,将“ 0”修饰符应用于指针对象仅可确保原子函数可用于已经存在的“ 0”变量。对于非易失性变量,添加限定符是免费的。我的错误是将原型中关键字的存在解释为使用该关键字的动机,而不是给使用它们的人员带来方便。     
        C ++ 11具有用于
volatile
和非
volatile
变量的原子。 如果编译器内部函数使用指向
volatile int
的指针,则意味着即使变量为volatile也可以使用它。它不会阻止您在非0数据上使用该功能。     
        好吧,关键字“ volatile”确保每次代码中出现该变量时,编译器始终将变量的值从内存中加载/存储。 这会阻止某些优化,例如该值只需一次加载到寄存器中,然后使用多次。 当您有多个线程可以修改线程之间的\'shared \'变量时,此选项很有用。您必须确保始终从内存中加载/存储该值,以检查其值可以被另一个线程修改。如果未使用volatile,则另一个线程可能未将新值写入内存(但将其放入寄存器或可能进行了其他某种优化),并且第一个线程不会注意到值的任何变化。 在您的情况下,“ volatile SInt32 * address \”告诉编译器,由地址指向的内存可能会受到任何源的更改。因此,需要原子操作。     

要回复问题请先登录注册