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