Да, блокировки C++ 11 являются атомарными: только один поток может иметь блокировку на одном std::mutex
за раз. В этом весь смысл мьютекса: обеспечить взаимное исключение.
Цель блокировки с двойной проверкой, чтобы избежать накладных расходов на приобретение блокировку, когда он не нужен: в частности, к избежать взаимного исключения и, как следствие сериализации, когда несколько потоков запустить один и тот же бит кода одновременно, и взаимное исключение больше не требуется.
Обычно это используется в какой-то форме одноразовой инициализации и по-прежнему требует синхронизации. Эта синхронизация может быть выполнена с помощью атомики, но ее трудно получить. Я упоминаю, как это сделать в сообщении в блоге, которое я написал (Lazy Initialization and Double Checked Locking with Atomics), но вам часто лучше использовать только местные static
объекты, или std::call_once
.
Мьютексы могут быть заменены на атомный флаг в некоторых случаях, но получение права на синхронизацию затруднено: обычно флаг является лишь некоторым признаком того, что к другим данным можно получить доступ, и вам необходимо убедиться, что эти данные правильно синхронизированы. Если профилирование не предполагает, что мьютекс является узким местом, вам лучше придерживаться мьютекса, чем пытаться перевернуть собственную синхронизацию с атомикой.
И наконец, основной точкой std::lock()
является блокировка более одного мьютексов за один раз без взаимоблокировки. Обычно вам нужно блокировать взаимные блокировки по одному за раз. Однако, если поток 1 блокирует mutex A, то mutex B, а поток 2 блокирует мьютекс B, а затем mutex A вы можете получить тупик. std::lock()
избегает этого, ожидая, пока поток не сможет получить оба мьютекса, не мешая другим потокам блокировать их в среднее время.