Используйте обычный паттерн, если вам действительно нужна двойная проверка блокировки
Причина
Двойная проверка блокировки легко может быть реализована неправильно. Если вам действительно нужно писать собственную двойную проверку блокировки, несмотря на правила CP.110: Не пишите свою собственную двойную проверку блокировки для инициализации и CP.100: Не используйте программирование без блокировок, если вам это совершенно необходимо, то делайте это в обычном паттерне.
Использования паттерна двойной проверки блокировки, которые не нарушают CP.110: Не пишите свою собственную двойную проверку блокировки для инициализации, возникают, когда действие, не являющееся потокобезопасным, одновременно сложно и редко, и существует быстрый потокобезопасный тест, который может гарантировать, что действие не требуется, но не может гарантировать обратное.
Пример, плохой
Использование volatile не делает первую проверку потокобезопасной, см. также CP.200: Используйте volatile только для общения с памятью, не являющейся C++
mutex action_mutex;
volatile bool action_needed;
if (action_needed) {
std::lock_guard<std::mutex> lock(action_mutex);
if (action_needed) {
take_action();
action_needed = false;
}
}
Пример, хороший
mutex action_mutex;
atomic<bool> action_needed;
if (action_needed) {
std::lock_guard<std::mutex> lock(action_mutex);
if (action_needed) {
take_action();
action_needed = false;
}
}
Тонко настроенный порядок памяти может быть полезным, когда загрузка с memory_order_acquire более эффективна, чем загрузка с последовательной консистентностью
mutex action_mutex;
atomic<bool> action_needed;
if (action_needed.load(memory_order_acquire)) {
lock_guard<std::mutex> lock(action_mutex);
if (action_needed.load(memory_order_relaxed)) {
take_action();
action_needed.store(false, memory_order_release);
}
}
Применение
??? Возможно ли обнаружить эту идиому?
CP.etc: Прочие правила параллелизма
Эти правила не поддаются простой категоризации: