Ответ NathanOliver не точен: mu
на самом деле статически инициализирован - это означает, что перед любой динамической инициализацией и, следовательно, также перед тем, как любой пользовательский код может вызвать mu.lock()
(напрямую или с помощью * 1005) *).
Тем не менее, ваш вариант использования безопасен - на самом деле, std::mutex
инициализация даже более безопасна, чем предполагалось в предыдущем ответе.
Причина этого заключается в том, что любая переменная со статической продолжительностью хранения (✓ check), которая инициализируется константным выражением (где вызов конструктора constexpr
явно рассматривается как таковой - ✓ check), равна инициализированная константа , которая является подмножеством статическая инициализация . Вся статическая инициализация происходит строго перед любой динамической инициализацией и, следовательно, перед тем, как ваша функция может быть вызвана в первый раз. ( basic.start.static / 2 )
Это относится к std::mutex
, потому что std::mutex
имеет только один жизнеспособный конструктор, конструктор по умолчанию, и он указан как constexpr
. ( thread.mutex.class )
Следовательно, в дополнение к обычной гарантии атомарности, что C ++ 11 и выше обеспечивает динамическую инициализацию статических переменных в области действия функции, другие std::mutex
экземпляры со статическим хранилищем также полностью не зависят от инициализации вопросы заказа, например:
#include <mutex>
extern std::mutex mtx;
unsigned counter = 0u;
const auto count = []{ std::lock_guard<std::mutex> lock{mtx}; return ++counter; };
const auto x = count(), y = count();
std::mutex mtx;
Если бы mtx
был динамически инициализирован, этот код проявил бы неопределенное поведение, потому что тогда инициализатор mtx
работал бы после того из динамически инициализированных x
и y
, и поэтому mtx
использоваться до его инициализации.
(В pthread или общих реализациях <thread>
, использующих pthread, этот эффект достигается использованием константного выражения PTHREAD_MUTEX_INITIALIZER
.)
PS: Это также верно для экземпляров std::atomic<T>
, если аргумент, передаваемый конструктору, является константным выражением. Это означает, например, что вы можете легко сделать спин-блокировку на основе std::atomic<IntT>
, которая невосприимчива к проблемам порядка инициализации. std::once_flag
имеет такое же желаемое свойство. std::atomic_flag
со статической продолжительностью хранения также может быть статически инициализирован двумя способами:
std::atomic_flag f;
, инициализируется нулями (из-за статической длительности хранения и из-за тривиального значения по умолчанию). Обратите внимание, что состояние флага, тем не менее, не определено, что делает этот подход довольно бесполезным.
std::atomic_flag f = ATOMIC_FLAG_INIT;
постоянно инициализируется и не устанавливается. Это то, что вы на самом деле хотите использовать.