Предположим, что код написан на C99, а целевая архитектура - x86.Модель сильной памяти x86 действует только на уровне машинного кода.С99 не имеет модели памяти.Я объясню, что может пойти не так, и расскажу, существует ли C99-совместимый способ решения проблем.
Во-первых, мы должны убедиться, что ни одна из переменных не оптимизирована, и что все обращаются к flag
, number1
и number2
происходят из памяти, а не кэшируются в регистрах ЦП 1 .Это может быть достигнуто в C99, квалифицируя все три переменные с volatile
.
Во-вторых, мы должны убедиться, что хранилище к flag
в первом потоке имеет семантику выпуска.Эта семантика включает две гарантии: сохранение в flag
не переупорядочивается при предыдущих обращениях к памяти и делает хранилище видимым для второго потока.Ключевое слово volatile
сообщает компилятору, что доступ к переменной может иметь видимые побочные эффекты.Это предотвращает изменение порядка доступа компилятором к изменчивым переменным по отношению к другим операциям, которые компилятор также имеет наблюдаемые побочные эффекты.То есть, создав все три переменные volatile
, компилятор будет поддерживать порядок всех трех хранилищ в первом потоке.Тем не менее, если есть другие доступы к энергонезависимой памяти, которые находятся выше или ниже хранилища до flag
, то такие доступы все еще могут быть переупорядочены.Таким образом, стандарт volatile
обеспечивает только семантику частичного выпуска.
В-третьих ... фактически для вашего конкретного фрагмента кода атомарность не требуется.Это связано с тем, что в хранилище flag
изменяется только один бит, который по своей природе является атомарным.Так что для этого конкретного кода вам не нужно беспокоиться об атомарности.Но в целом, если хранилище на flag
может измениться более чем на один бит, и если условие, проверенное во втором потоке, может вести себя по-разному в зависимости от того, видит ли он все или некоторые изменения в битах, то вам, безусловно, нужно убедиться, чточто доступ к 'flag` является атомарным.К сожалению, в C99 нет понятия атомарности.
Чтобы получить полную семантику и атомарность релиза, вы можете использовать атомарность C11 (как обсуждалось в статье, которую вы цитировали), или вы можете прибегнуть к методам, специфичным для компилятора (также обсуждаемымв статье, которую вы цитировали).Конечно, вы все равно можете просто посмотреть на сгенерированный машинный код и посмотреть, предлагает ли сама модель памяти x86 необходимые требования для корректности.Это невозможно на больших базах кода.Кроме того, при следующей компиляции кода сгенерированный машинный код может измениться.Наконец, поскольку вы всего лишь человек, вы можете ошибиться.
(1) В цитируемой статье переменная A
объявлена как общая глобальная переменная.Теперь, скорее всего, компилятор выделит его из памяти.Но соответствует ли это строгому стандарту?Что мешает компилятору размещать его в регистре на протяжении всего времени жизни программы?Не уверен насчет этого.