Предположим, требуется организовать защиту произвольного кода локальным статическим мьютексом:
void MyClass::myMethod() {
static QMutex mutex;
mutex.lock();
// защищённый код ...
mutex.unlock();
}
Насколько мне известно, если вместо QMutex будет иной локальный статический тип (например, QString), то запросто может случиться "гонка" и, скажем, два конкурирующих потока создадут две копии объекта.
Несмотря на то, что в справке Qt для QMutex указано, что все методы являются потокобезопасными, нет уверенности, что заявленное справедливо и для его конструктора.
Будет ли в контексте MyClass::myMethod() создание объекта мьютекса при первом обращении потокобезопасным?
Ответ
Начиная с C++11 инициализация статических переменных потокобезопасна (§6.7):
Otherwise such a variable is initialized the
first time control passes through its declaration; such a variable is
considered initialized upon the completion of its initialization. If
the initialization exits by throwing an exception, the initialization
is not complete, so it will be tried again the next time control
enters the declaration. If control enters the declaration
concurrently while the variable is being initialized, the concurrent
execution shall wait for completion of the initialization
Для более ранних стандартов это не гарантировалось, хотя некоторые компиляторы обеспечивали такую защиту (например, GCC).
Рассмотрим конструктор QMutex
QMutex::QMutex(RecursionMode mode)
{
d_ptr.store(mode == Recursive ? new QRecursiveMutexPrivate : 0);
}
Qt широко использует идиому Pimpl, d_ptr как раз и является указателем на реализацию и имеет тип QBasicAtomicPointer
Другая, более серьезная, проблема связана с race condition: например, два потока A и B начинают инициализацию переменной mutex, поток A завершает инициализацию и вызывает функцию lock(), управление передается потоку B, который в свою очередь завершает инициализацию, что приводит в итоге к неопределенному поведению.
Комментариев нет:
Отправить комментарий