Win32/pthreads线程函数上的volatile正确性
volatile-Correctness on Win32/pthreads Threading Functions
在阅读了这篇精彩的文章后,我开始四处挖掘,以volatile更正一些代码。volatile正确性的一个结果(正如我所理解的)是,从不同线程访问的方法应该是volatile限定的。
一个简单的例子可能是使用Win32或pthreads的互斥。我们可以制作一个类Mutex
,并给它一个字段:
#if defined BACKEND_WIN32
CRITICAL_SECTION _lock;
#elif defined BACKEND_POSIX
pthread_mutex_t _lock;
#endif
".acquise()"方法可能看起来像:
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)":无法将参数1从"volatile CRITICAL_SECTION*"转换为"LPCRITICAL_SECTION"
来自g++:
错误:从"volatile pthread_mutex_t*"到"pthread_mutex_t*'[-fpermission]的转换无效
尝试解锁_lock
时也会遇到类似的问题。两个问题:
- 我的印象是,这些只是API过时的产物。事实上是这样吗?我误解什么了吗
- 锁定或解锁API函数的整个目的是在关键部分之间切换,因此,在传递到这些函数之前,我是否应该只使用名称不适当的
const_cast
来丢弃_lock
的volatile
,而不会产生不良影响
我认为volatile
对标准(现在)意图的最佳总结可在n3797 S7.1.6.1/6/7 中找到
对具有volatile限定类型的对象的访问是由实现定义的。如果试图通过使用非易失性限定类型的glvalue来引用用易失性定义类型定义的对象,则程序行为是未定义的。
[注意:volatile是对实现的一个提示,以避免涉及对象的激进优化,因为对象的值可能会通过实现无法检测到的方式进行更改。此外,对于某些实现,volatile可能表示访问对象需要特殊的硬件指令。有关详细语义,请参阅1.9。通常,volatible的语义在C++中和在C中是一样的。--尾注]
可悲的是,这意味着一个符合标准的实施不需要做该条款所暗示的那么多。没有义务立即存储值,也没有义务重新加载可能已更改的值。编译器不能自由地消除加载它尚未存储的值的代码,但可以自由地消除存储它从未使用过的值的码。
如注释中所述,最好使用实现定义的API或atomic
或mutex
。易失性会让你失望。斯特劳斯特鲁普在相关问题中说了同样多的话。
- 代理对象的常量正确性
- 在一个读写器队列中,我可以用volatile替换原子吗
- std::函数常量正确性未遵循
- 违反const正确性:我应该现实地期待什么问题
- C++ 常量正确性/缺少支持常量和非常量实例的类的常量构造函数
- GCC 8 无法编译make_shared<volatile int>()
- 不能使这种类型的"void(C::* volatile)(int) const "在参考手册C++示
- 如何使用"asm volatile"编写 btr 指令
- 自定义引用包装器的常量正确性
- 未能优化看似明显的循环不变量(但volatile限定符发挥了神奇的作用)
- 读取互斥对象范围之外的volatile变量,而不是std::atomic
- 你能从"volatile const char*"构造一个字符串吗?(不使用`const_cast`
- volatile, std::sig_atomic_t, and atomic_signal_fence
- 为什么std::atomic中的所有成员函数都同时出现在有volatile和没有volatile的情况下
- 引用类型的数据成员提供有关恒常正确性"loophole"
- 初始化期间针对安全检查的指针的恒定正确性
- 如果我公开常量和非常量 API,我是否破坏了常量正确性?
- 了解C++中的'volatile'关键字
- 如何强制实施有关指针数据成员的常量正确性
- Win32/pthreads线程函数上的volatile正确性