многопоточность — реализация двойной проверенной блокировки в C ++ 98/03 с использованием volatile

чтение этот В статье о шаблоне блокировки с двойной проверкой в ​​C ++ я дошел до места (стр. 10), где авторы демонстрируют одну из попыток «правильно» реализовать DCLP с помощью volatile переменные:

class Singleton {
public:
static volatile Singleton* volatile instance();

private:
static volatile Singleton* volatile pInstance;
};

// from the implementation file
volatile Singleton* volatile Singleton::pInstance = 0;
volatile Singleton* volatile Singleton::instance() {
if (pInstance == 0) {
Lock lock;
if (pInstance == 0) {
volatile Singleton* volatile temp = new Singleton;
pInstance = temp;
}
}
return pInstance;
}

После такого примера есть фрагмент текста, который я не понимаю:

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

и позже:

… Абстрактная машина C ++ однопоточная, и компиляторы C ++ могут
выбрать генерацию небезопасного кода из исходного кода, как указано выше,
тем не мение.

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

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

1

Решение

Я думаю, что они имеют в виду проблема когерентности кэша обсуждается в разделе 6 («DCLP на многопроцессорных машинах». В многопроцессорной системе аппаратное обеспечение процессора / кэша может записать значение для pInstance до того, как значения выписаны для выделенного Singleton, Это может привести к тому, что второй процессор увидит ненулевое значение pInstance прежде чем он сможет увидеть данные, на которые он указывает.

Для этого требуется инструкция аппаратного ограничения, чтобы убедиться, что вся память обновлена, прежде чем другие процессоры в системе смогут ее увидеть.

1

Другие решения

Указатель на синглтон может быть изменчивым, но данные в синглтон нет.

Представь, что у Синглтона есть int x, y, z; в качестве участников, установите 15, 16, 17 в конструкторе по какой-то причине.

  volatile Singleton* volatile temp = new Singleton;
pInstance = temp;

в порядке, temp написано раньше pInstance, Когда же x,y,z написано относительно тех? До? После? Вы не знаете Они не являются изменчивыми, поэтому их не нужно упорядочивать относительно изменчивого порядка.

Теперь приходит поток и видит:

if (pInstance == 0) {  // first check

И скажем pInstance был установлен, не является нулевым.
Каковы ценности x,y,z? Даже если new Singleton был вызван, и конструктор «запустить», вы не знаете, операции, которые устанавливают x,y,z бегали или нет.

Так что теперь ваш код идет и читает x,y,z и вылетает, потому что это действительно ожидало 15,16,17, не случайные данные.

Ой, подожди, pInstance является изменчивым указателем на изменчивые данные! Так x,y,z волатильно верно? Правильно? И таким образом заказал с pInstance а также temp, Ага!

Почти. Любые чтения из *pInstance будет изменчивым, но строительство через new Singleton не был изменчивым. Таким образом, первоначальный пишет в x,y,z не были заказаны. 🙁

Так что вы мог, может быть, сделать членов volatile int x, y, z; ХОРОШО. Тем не мение…

C ++ сейчас имеет модель памяти, даже если это не было, когда статья была написана. Согласно действующим правилам, volatile не мешает гонкам данных. volatile не имеет ничего общего с потоками. Программа UB. Кошки и собаки живут вместе.

Кроме того, хотя это раздвигает пределы стандарта (то есть становится неопределенным в отношении того, что volatile на самом деле означает), всезнающий, всевидящий, полностью оптимизирующий программы компилятор может взглянуть на ваше использование volatile и сказать: «Нет, эти летучие компоненты на самом деле не подключаются к каким-либо адресам памяти ввода-вывода и т. д., они на самом деле не наблюдаемое поведение, я просто сделаю их энергонезависимыми» …

1

Если я правильно понимаю, они говорят, что в контексте однопоточной абстрактной машины компилятор может просто преобразовать:

volatile Singleton* volatile temp = new Singleton;
pInstance = temp;

В:

pInstance = new Singleton;

Потому что наблюдаемое поведение остается неизменным. Затем это возвращает нас к первоначальной проблеме с двойной проверкой блокировки.

0
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector