Я знаю, в нескольких вопросах/ответах было сказано довольно ясно, что volatile
связано с видимым состоянием модели памяти С++, а не с многопоточным.
С другой стороны, эта статья Alexandrescu использует ключевое слово volatile
не как функцию времени исполнения, а скорее как проверку времени компиляции на заставить компилятор не принимать код, который не может быть потокобезопасным. В статье ключевое слово используется скорее как тег 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_
};
Примечание
После появления первой пары ответов я думаю, что я должен уточнить, поскольку я, возможно, не использовал наиболее подходящие слова.
Использование volatile
происходит не из-за того, что он предоставляет во время выполнения, а из-за того, что он означает во время компиляции. То есть такой же трюк можно потянуть с помощью ключевого слова const
, если он был так же редко используется в пользовательских типах, как volatile
. То есть, есть ключевое слово (которое, случается, записывается волатильно), что позволяет мне блокировать вызовы функций-членов, а Alexandrescu использует его, чтобы обмануть компилятор, чтобы не скомпилировать небезопасный код.
Я вижу в нем множество тэков метапрограммирования, которые не существуют из-за того, что они делают во время компиляции, а скорее за то, что заставляет компилятор делать для вас.