Я использую pthread для создания потока в классе — обертке с полем bool mAnyField. В главной функции потока «A» очень большое число раз проверяю состояние этого поля и если поле false завершаю выполнение. Собственно вопрос — можно ли из другого потока «B» поменять это поле без синхронизации и соответственно читать поле (в потоке «A») тоже без синхронизации? Чем это черевато? Не хочу использовать синхронизацию только лишь из вопросов производительности.
Кстати, общее правило: «синхронизация» нужна только в том случае, если существует риск нарушения логики работы других потоков (т.е. в момент изменения структуры данных могут находится в противоречивом состоянии, оперирование с которым может нарушить правильную логику).
В Вашем же случае, когда «поток A только читает bool а поток B только пишет» в принципе нарушение логики невозможно (поскольку для «железных типов данных», вроде bool не существует такого состояния, которое, бы нарушило логику потока A).Вернее, нарушение логики потока A, конечно возможно, но только в единственном случае — если вы забыли «volatile» и поток A будет продолжать использовать считанное значение переменной, вместо перечитывания обновленного значения.
gribozavr, да в «общем случае», обращение на чтение к переменной которую пишет другой поток, это конечно «гонка». Но, в описанном случае, булевая переменная это всего лишь «флаг по которому завершается поток», соответственно, логике «просто негде нарушится» (если говорить про конкретный случай).
Но иногда стоит и перестраховаться, если вместо «тупого завершения работы потока» в один прекрасный день там, вместо завершения возникнет код, чувствительный к переупорядочиванию чтения процом. Так что, да, барьер желателен. Благо, перестраховаться можно, барьер — это достаточно «дешево» для проца.
Другой поток-таки запись увидит, и гарантией выступает такая «хитрая вещь» как «синхронизация кешей». Именно благодаря ей, когда одно из процессорных ядер «засирает кеш-линию», если другие ядра, к которых тоже была эта кеш линия в кеше, просто перечитывают новое значение. Если бы этого «гаранта» не было, многоядерные процессоры просто не могли бы существовать.
Собственно, барьер это «граница в машинном коде», через которую запрещается «перетаскивать» порядок чтения/записи. Вещь чрезвычайно полезная, поскольку иногда без нее правильность логики действительно нельзя гарантировать.
А *volatile* для таких совместно используемых данных действительно необходим, поскольку именно он заставляет перечитывать данные из кеша, вместо того, чтобы положить в регистр (на который синхронизация кешей не распостраняется, и соотвественно, поток, считавший переменную *в регистр* действительно никогда не увидит больше, что переменная была изменена другим потоком).
Барьер памяти тут не причем, тут нет синхронизации. Тут просто один флаг. Так что конкретно а этой ситуации, по идее, должно всё работать нормально. НО если есть возможность, то я бы лучше использовал std::atomic с memory_order_relaxed
Когерентность кешей. Да, а если её нет? ОК, таких машин мало, согласен.
> А *volatile* для таких совместно используемых данных действительно необходим
Он не даёт гарантий того, что данные будут считаны из памяти. Он даёт гарантию того, что в машинном коде будет load, а как он будет reorder'иться или будет ли обслужен из кеша — это уже как процессору захочется.