Можно ли считать один лишь release-фенс средством публикации обычных данных другому потоку?
Нет. Release-фенс сам по себе не устанавливает связь с другим потоком и не делает обычные данные автоматически видимыми ему. Для публикации нужен атомарный объект, через который другой поток наблюдает результат: например, release-фенс перед атомарной записью и acquire-фенс после атомарного чтения, увидевшего эту запись.
Фенсы появились как более гибкий инструмент управления порядком операций, чем фиксированные варианты памяти у каждой атомарной операции. Они позволяют отделить упорядочивание обычных обращений к памяти от атомарного обмена служебным состоянием.
Такая гибкость полезна в низкоуровневых алгоритмах, например в очередях без блокировок. Однако цена гибкости — необходимость самостоятельно доказать, что фенсы связаны через конкретную атомарную операцию.
Пусть один поток записывает данные, затем выполняет release-фенс, а другой поток выполняет acquire-фенс. Между ними нет атомарного объекта, значение которого один поток изменил, а другой прочитал.
В этом случае отсутствует требуемое отношение synchronizes-with, а значит, не возникает и гарантированного happens-before от записи данных к их чтению. Если обычные данные одновременно читаются и изменяются без такого отношения, программа может содержать гонку данных и иметь неопределённое поведение.
Корректная схема состоит из трёх частей:
Если атомарное чтение действительно видит значение, записанное операцией публикации, стандарт C++ связывает release-фенс с acquire-фенсом. Тогда записи, предшествующие release-фенсу, становятся happens-before чтений, следующих за acquire-фенсом.
Здесь атомарный флаг является не хранилищем данных, а каналом связи между фенсами. Расслабленный порядок у операций с флагом допустим именно потому, что упорядочивание публикации и чтения обеспечивают фенсы, а не сами store и load.
На практике release-запись и acquire-чтение часто предпочтительнее: они проще для проверки и уменьшают риск ошибиться в правилах связывания фенсов. Фенсы могут дать нужную производительность или структуру в специализированном алгоритме, но требуют строгого доказательства порядка и правильного значения, наблюдаемого атомарным чтением.
Важно, что фенс не превращает обычную переменную в атомарную. Он только упорядочивает обращения и, при наличии корректной межпоточной связи, помогает установить видимость. Если несколько потоков одновременно изменяют обычные данные либо потребитель может прочитать их до успешной публикации, фенс проблему не решает.
В однопроизводительной очереди без блокировок производитель записывает элементы в буфер, а затем публикует новый индекс готовой границы. Потребитель читает индекс и после этого обращается к опубликованным элементам.
Возможны два подхода:
Для общего прикладного кода выбирается первый вариант: атомарная публикация с release и чтение с acquire. Фенсы оправданы в тщательно протестированном низкоуровневом компоненте, где измерения показывают пользу, а протокол закреплён формальным анализом. Это снижает риск получить редкую ошибку из-за неверного связывания фенсов.
Вопрос: Достаточно ли acquire-фенса после любого чтения атомарного флага, чтобы безопасно прочитать данные?
Ответ: Нет. Чтение должно увидеть значение, связанное с публикацией производителя, обычно значение, записанное его атомарной операцией, либо допустимое значение из соответствующей release-последовательности. Если флаг уже содержит старое значение или протокол допускает ложное условие готовности, acquire-фенс не создаёт нужного отношения happens-before.
Вопрос: Чем схема с release/acquire-операциями над атомарным флагом проще схемы с фенсами?
Ответ: В схеме с release-записью и acquire-чтением сама пара операций явно выражает публикацию и получение публикации. Не требуется отдельно проверять порядок фенса относительно атомарной операции и условие, что чтение наблюдает нужную запись. Поэтому такой вариант обычно легче ревьюить и адаптировать, хотя он может быть менее гибким в специализированных алгоритмах.
Вопрос: Защищает ли release-фенс от записи данных, выполненной после него?
Ответ: Нет. Release-фенс упорядочивает операции, предшествующие ему, относительно последующей атомарной публикации. Запись, выполненная после фенса, не становится частью опубликованного набора данных и может быть прочитана потребителем без гарантированного happens-before. Все данные, которые должны быть видимы после публикации, необходимо записать до release-фенса.