在阅读this wonderful article之后,我开始深入研究一些代码。挥发性正确性的一个结果(据我所知)是从不同线程访问的方法应该是volatile限定的。
一个简单的例子可能是使用Win32或pthreads的互斥锁。我们可以创建一个类Mutex
并给它一个字段:
#if defined BACKEND_WIN32
CRITICAL_SECTION _lock;
#elif defined BACKEND_POSIX
pthread_mutex_t _lock;
#endif
" .acquire()"方法可能如下所示:
void Mutex::acquire(void) volatile {
#if defined BACKEND_WIN32
EnterCriticalSection(&_lock);
#elif defined BACKEND_POSIX
pthread_mutex_lock(&_lock);
#endif
}
尝试这个并不奏效。来自MSVC:
错误C2664:' void EnterCriticalSection(LPCRITICAL_SECTION)' :无法从< volatile CRITICAL_SECTION *'转换参数1到' LPCRITICAL_SECTION'
来自g ++:
错误:从'volatile pthread_mutex_t *'无效转换为'pthread_mutex_t *'[-fpermissive]
您尝试解锁_lock
时会遇到类似问题。两个问题:
const_cast
的情况在传递给这些函数之前抛弃了volatile
的{{1}} - _lock
,没有任何不良影响?答案 0 :(得分:1)
我认为volatile
标准(现在)的最佳总结可以在n3797 S7.1.6.1 / 6/7中找到
对具有volatile限定类型的对象的访问构成是实现定义的。如果尝试通过使用具有非volatile限定类型的glvalue来引用使用volatile限定类型定义的对象,则程序行为是未定义的。
[注意:volatile是对实现的暗示,以避免涉及对象的激进优化,因为对象的值可能会被实现无法检测到的更改。此外,对于某些实现,volatile可能指示访问对象需要特殊的硬件指令。有关详细语义,请参见1.9。一般来说,volatile的语义在C ++中与在C语言中的含义相同。-end note]
可悲的是,这意味着符合标准的实施不需要像该文章所暗示的那样做。没有义务及时存储值,并且有义务重新加载可能已更改的值。编译器不能自由地加载加载它尚未存储的值的代码,但可以自由地删除存储它从未使用过的值的代码。
如评论中所述,最好使用实现定义的API或atomic
或mutex
。挥发会让你失望。 Stroustrup在链接的question中说了很多。