可以在用户定义的类型中使用volatile来帮助编写线程安全的代码

Dav*_*eas 25 c++ volatile thread-safety

我知道,之前的几个问题/答案已经很明确了,这volatile与c ++内存模型的可见状态有关,而与多线程无关.

另一方面,Alexandrescu的这篇文章使用volatile关键字not作为运行时特性,而是使用编译时检查来强制编译器无法接受可能不是线程安全的代码.在文章中,关键字的使用更像是required_thread_safety标签,而不是实际的预期用途volatile.

这(ab)使用volatile合适吗?方法中可能隐藏了哪些可能的陷阱?

首先想到的是增加了混乱:volatile与线程安全无关,但由于缺乏更好的工具,我可以接受它.

文章的基本简化:

如果声明一个变量volatile,则只能volatile在其上调用成员方法,因此编译器将阻止调用其他方法的代码.声明std::vector实例volatile将阻止该类的所有使用.添加一个执行const_cast释放volatile需求的锁定指针形状的包装器,将允许通过锁定指针进行任何访问.

窃取文章:

template <typename T>
class LockingPtr {
public:
   // Constructors/destructors
   LockingPtr(volatile T& obj, Mutex& mtx)
      : pObj_(const_cast<T*>(&obj)), pMtx_(&mtx)
   { mtx.Lock(); }
   ~LockingPtr()   { pMtx_->Unlock(); }
   // Pointer behavior
   T& operator*()  { return *pObj_; }
   T* operator->() { return pObj_; }
private:
   T* pObj_;
   Mutex* pMtx_;
   LockingPtr(const LockingPtr&);
   LockingPtr& operator=(const LockingPtr&);
};

class SyncBuf {
public:
   void Thread1() {
      LockingPtr<BufT> lpBuf(buffer_, mtx_);
      BufT::iterator i = lpBuf->begin();
      for (; i != lpBuf->end(); ++i) {
         // ... use *i ...
      }
   }
   void Thread2();
private:
   typedef vector<char> BufT;
   volatile BufT buffer_;
   Mutex mtx_; // controls access to buffer_
};
Run Code Online (Sandbox Code Playgroud)

注意

在出现前几个答案之后,我想我必须澄清一下,因为我可能没有使用最合适的词.

使用volatile它不是因为它在运行时提供的内容,而是因为它在编译时的含义.也就是说,const如果在用户定义类型中很少使用关键字,则可以使用关键字拉出相同的技巧volatile.也就是说,有一个关键字(恰好是拼写易失性)允许我阻止成员函数调用,而Alexandrescu正在使用它来欺骗编译器无法编译线程不安全的代码.

我认为很多元编程技巧不是因为它们在编译时所做的,而是因为它迫使编译器为你做的事情.

Abh*_*hay 6

我认为问题不在于提供的线程安全性volatile.它没有,安德烈的文章也没有说它确实如此.这里,a mutex用于实现这一点.问题是,是否使用volatile关键字来提供静态类型检查以及使用互斥锁进行线程安全代码是滥用volatile关键字?恕我直言,它非常聪明,但我遇到的开发人员不仅仅是为了它而不是严格类型检查的粉丝.

IMO在为多线程环境编写代码时,已经有足够的谨慎强调,你会期望人们不要不了解竞争条件和死锁.

这种包装方法的缺点是,使用包装的类型的每个操作都LockingPtr必须通过成员函数.这将增加一个间接水平,这可能会显着影响开发人员在团队中的舒适度.

但如果你是一个纯粹的人,他相信C++的精神,即严格的类型检查 ; 这是一个很好的选择.


Mal*_*glu 0

你最好不要这样做。易失性甚至不是为了提供线程安全而发明的。它的发明是为了正确访问内存映射的硬件寄存器。volatile关键字对 CPU 的乱序执行功能没有影响。您应该使用正确的操作系统调用或 CPU 定义的 CAS 指令、内存栅栏等。

中科院

记忆栅栏

  • 如果您仔细阅读本文和代码,您会发现该变量在使用时不是“易失性”(通过“const_cast”删除限定符)。这只是一个编译时技巧。正如 @Malkocoglu 指出的那样,整个线程安全性是通过互斥体正确处理的。我认为混乱是该方法的一大缺点 (2认同)