Win32/pthreads线程函数上的volatile正确性

volatile-Correctness on Win32/pthreads Threading Functions

本文关键字:volatile 正确性 函数 pthreads 线程 Win32      更新时间:2023-10-16

在阅读了这篇精彩的文章后,我开始四处挖掘,以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来丢弃_lockvolatile,而不会产生不良影响

我认为volatile对标准(现在)意图的最佳总结可在n3797 S7.1.6.1/6/7 中找到

对具有volatile限定类型的对象的访问是由实现定义的。如果试图通过使用非易失性限定类型的glvalue来引用用易失性定义类型定义的对象,则程序行为是未定义的。

[注意:volatile是对实现的一个提示,以避免涉及对象的激进优化,因为对象的值可能会通过实现无法检测到的方式进行更改。此外,对于某些实现,volatile可能表示访问对象需要特殊的硬件指令。有关详细语义,请参阅1.9。通常,volatible的语义在C++中和在C中是一样的。--尾注]

可悲的是,这意味着一个符合标准的实施不需要做该条款所暗示的那么多。没有义务立即存储值,也没有义务重新加载可能已更改的值。编译器不能自由地消除加载它尚未存储的值的代码,但可以自由地消除存储它从未使用过的值的码。

如注释中所述,最好使用实现定义的API或atomicmutex。易失性会让你失望。斯特劳斯特鲁普在相关问题中说了同样多的话。