Подтвердить что ты не робот

Двойной контрольный шаблон блокировки

В С++ и опасностях блокировки с двойным проверкой, там persudo-код правильно реализует шаблон, который предлагается авторами. См. Ниже,

Singleton* Singleton::instance () {
    Singleton* tmp = pInstance;
    ... // insert memory barrier (1)
    if (tmp == 0) {
        Lock lock;
        tmp = pInstance;
        if (tmp == 0) {
            tmp = new Singleton;
            ... // insert memory barrier (2)
            pInstance = tmp;
        }
    }
    return tmp;
}

Мне просто интересно, может ли первый барьер памяти перемещаться прямо над оператором return?

EDIT: Другой вопрос: в связанной статье, как vidstige, указан

Технически вам не нужны полные двунаправленные барьеры. Первый барьер должно препятствовать нисходящей миграции конструкции Синглтонов (через другой поток); второй барьер должен препятствовать миграции вверх инициализации pInstances. Они называются "приобретать" и "освободить" операции и может обеспечить лучшую производительность, чем полная барьеры на оборудовании (например, Itainum), который делает различие.

В нем говорится, что второй барьер не должен быть двунаправленным, поэтому как он может препятствовать перемещению pInstance перед этим барьером? Несмотря на то, что первый барьер может предотвратить миграцию вверх, но в другом потоке все еще есть шанс увидеть неинициализированные элементы.

EDIT: Я думаю, что я почти понимаю цель первого барьера. Как отметил sonicoder, предсказание ветвления может привести к тому, что tmp будет NULL, если if вернет true. Чтобы избежать этой проблемы, должен быть обнаружен барьер, препятствующий чтению tmp взамен до чтения в if.Забастовкa >

Первый барьер сопряжен со вторым барьером для достижения синхронизации с отношением, поэтому он может двигаться вниз.

EDIT. Для тех, кто интересуется этим вопросом, я настоятельно рекомендую читать memory-barriers.txt.

4b9b3361

Ответ 1

Я не видел никакого правильного ответа здесь, связанного с вашим вопросом, поэтому я решил опубликовать его даже после более чем трех лет;)

Мне просто интересно, можно ли перенести первый барьер памяти прямо над выражением возврата?

Да, это возможно.

Он для потоков, которые не будут вводить оператор if, т.е. pInstance, уже был правильно сконструирован и инициализирован и виден.

Второй барьер (один прямо перед pInstance = tmp;) гарантирует, что инициализация полей однопользовательских элементов будет сохранена до памяти до pInstance = tmp;. Но это НЕ означает, что другие потоки (на других ядрах) будут видеть эти эффекты памяти в том же порядке (контр-интуитивный, верно?). Второй поток может видеть новое значение указателя в кеше, но не те поля членов. Когда он обращается к элементу путем разыменования указателя (например, p->data), адрес этого элемента может быть уже в кеше, но не тот, который вам нужен. Взрыв! Прочтены неверные данные. Обратите внимание, что это больше, чем теоретическое. Существуют системы, для которых требуется выполнить команду когерентности кеша (например, барьер памяти) для вывода новых данных из памяти.

Вот почему первый барьер существует. Это также объясняет, почему это нормально, чтобы разместить его прямо перед оператором return (но это должно быть после Singleton* tmp = pInstance;).

В нем говорится, что второй барьер не должен быть двунаправленным, поэтому как это может помешать присвоению pInstance от перемещения до этот барьер?

Барьер записи гарантирует, что каждая запись, предшествующая ей, будет эффективно выполняться до каждой записи после нее. Это знак остановки, и никакая запись не может пересечь его с другой стороны. Более подробное описание приведено здесь здесь.

Ответ 2

Нет, барьер памяти нельзя перемещать ниже оператора присваивания, поскольку барьер памяти защищает задание от миграции вверх. Из связанной статьи:

Первый барьер должен предотвратить нисходящая миграция синглтонов строительство (другой резьбой); второй барьер должен препятствовать миграция pInstances инициализации.

На стороне примечания: двойные проверки блокировки для одиночных игр полезны только в том случае, если у вас есть огромные требования к производительности.

Профилировали ли вы свои двоичные файлы и наблюдали за доступом к синглону в виде бутылочной шее? Если нет шансов, вам вообще не нужно беспокоиться о двойном проверке шаблона блокировки.

Я рекомендую использовать простую блокировку.