Можно ли переупорядочить два магазина в такой одноэлементной реализации? - PullRequest
0 голосов
/ 01 июля 2018

В следующей единственной функции get, могут ли другие потоки видеть instance как не-ноль, но almost_done все еще false? (Скажем, almost_done изначально false.)

Singleton *Singleton::Get() {
    auto tmp = instance.load(std::memory_order_relaxed);
    std::atomic_thread_fence(std::memory_order_acquire);
    if (tmp == nullptr) {
        std::lock_guard<std::mutex> guard(lock);
        tmp = instance.load(std::memory_order_relaxed);
        if (tmp == nullptr) {
            tmp = new Singleton();
            almost_done.store(true, std::memory_order_relaxed); // 1
            std::atomic_thread_fence(std::memory_order_release);
            instance.store(tmp, std::memory_order_relaxed); // 2
        }
    }
    return tmp;
}

Если они могут, то почему? Каково обоснование?

Я знаю, что ничто не может "выйти" из секции получения-выпуска, но не может 2 войти в нее и изменить порядок с 1?

Я знаю, что мне не нужны такие сложные методы для поточно-ориентированных синглетонов в C ++, и да, в almost_done нет особого смысла, это чисто для обучения.

1 Ответ

0 голосов
/ 02 июля 2018

В вашем коде показана действительная реализация шаблона двойной проверки блокировки (DCLP).
Синхронизация обрабатывается либо std::mutex, либо std::atomic::instance в зависимости от порядка, в котором потоки вводят код.

могут ли другие потоки видеть экземпляр как ненулевой, но почти_данный все еще ложный?

Нет, это невозможно.

Шаблон DCLP гарантирует, что все потоки, которые выполняют загрузку (которая возвращает ненулевое значение) в начале, гарантированно увидят instance точку в допустимой памяти и almost_done==true потому что загрузка синхронизирована с релизом магазина.

Причина, по которой можно подумать, что это возможно, заключается в небольшом окне возможностей, где первый поток (# 1) содержит std::mutex, а второй поток (# 2) входит в первое if -статмент ,

Перед тем как # 2 заблокирует std::mutex, он может наблюдать значение для instance (все еще указывая на несинхронизированную память, потому что мьютекс отвечает за это, но еще не синхронизировал).
Но даже если это произойдет (действительный сценарий в этом шаблоне), # 2 увидит almost_done==true, так как ограничитель освобождения (вызываемый # 1) приказывает расслабленному магазину almost_done до того, как магазин расслабился до instance, и этот же порядок наблюдается другими потоками.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...