如何解决如果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 \”告诉编译器,由地址指向的内存可能会受到任何源的更改。因此,需要原子操作。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。