В стандарте C ++ нет потоков, и Потоки не могут быть реализованы в виде библиотеки .
Поэтому стандарт ничего не говорит о поведении программ, использующих потоки. Вы должны посмотреть, какие дополнительные гарантии предоставляет ваша реализация потоков.
Тем не менее, в поточных реализациях, которые я использовал:
(1) да, вы можете предположить, что не относящиеся к делу значения не записываются в переменные. В противном случае вся модель памяти уходит в окно. Но будьте осторожны, когда вы говорите, что «другая нить» никогда не устанавливает b
в false, это означает, что где угодно, когда угодно. Если это произойдет, то эта запись может быть переупорядочена для выполнения во время вашего цикла.
(2) нет, компилятор может переупорядочить присваивания b1 и b2, поэтому возможно, что b1 в конечном итоге окажется true, а b2 false. В таком простом случае я не знаю, почему он будет переупорядочен, но в более сложных случаях могут быть очень веские причины.
[Edit: упс, к тому времени, как я добрался до ответа (2), я забыл, что b было изменчивым. Чтения из энергозависимой переменной не будут переупорядочены, извините, так что да, в типичной реализации многопоточности (если есть такая вещь), вы можете предположить, что вы не получите b1 true и b2 false.]
(3) так же, как 1. volatile
в общем случае не имеет ничего общего с многопоточностью. Однако в некоторых реализациях (Windows) это довольно увлекательно и может фактически подразумевать барьеры памяти.
(4) в архитектуре, в которой запись int
является атомарной, да, хотя volatile
не имеет к этому никакого отношения. Смотри также ...
(5) внимательно проверьте документы. Вероятно, да, и снова volatile не имеет значения, потому что почти на всех архитектурах int
записи являются атомарными. Но если int
запись не является атомарной, то нет (и нет для предыдущего вопроса), даже если она нестабильна, вы в принципе могли бы получить другое значение. Однако, учитывая эти значения 7 и 8, мы говорим о довольно странной архитектуре для байта, содержащего соответствующие биты, которые должны быть записаны в два этапа, но с другими значениями вы могли бы более правдоподобно получить частичную запись.
В качестве более правдоподобного примера, предположим, что по какой-то странной причине у вас есть 16-битное int на платформе, где только 8-битные записи являются атомарными. Странно, но законно, и, поскольку int
должно быть не менее 16 бит, вы можете увидеть, как это могло произойти. Предположим далее, что ваше начальное значение равно 255. Тогда приращение может быть юридически реализовано как:
- прочитать старое значение
- приращение в регистре
- запишите старший байт результата
- записать младший значащий байт результата.
Поток только для чтения, который прерывал инкрементный поток между третьим и четвертым шагами этого, мог видеть значение 511. Если записи в другом порядке, он мог видеть 0.
Несовместимое значение может остаться навсегда, если один поток пишет 255, другой поток одновременно записывает 256, и записи чередуются. Невозможно на многих архитектурах, но чтобы знать, что этого не произойдет, нужно знать хотя бы кое-что об архитектуре. Ничто в стандарте C ++ не запрещает этого, потому что стандарт C ++ говорит о том, что выполнение прерывается сигналом, но в остальном не имеет понятия прерывания выполнения другой частью программы, а также понятия одновременного выполнения. Вот почему потоки - это не просто еще одна библиотека - добавление потоков в корне меняет модель выполнения C ++. Требуется, чтобы реализация работала по-другому, поскольку в конечном итоге вы обнаружите, например, используете ли вы потоки в gcc и забыли указать -pthreads
.
То же самое может произойти на платформе, где выровненные int
записи являются атомарными, но не выровненные int
записи разрешены и не атомарны.Например, IIRC на x86, невыровненные записи int
не гарантируются атомарными, если они пересекают границу строки кэша.Компиляторы x86 не будут неправильно выравнивать объявленную переменную int
, по этой и другим причинам.Но если вы играете в игры со структурной упаковкой, вы, вероятно, можете привести пример.
Итак: практически любая реализация даст вам необходимые гарантии, но может сделать это довольно сложным способом.
В общем, я обнаружил, что не стоит пытаться полагаться на специфичные для платформы гарантии доступа к памяти, которые я до конца не понимаю, чтобы избежать мьютексов.Используйте мьютекс, и если это слишком медленно, используйте высококачественную структуру без блокировки (или реализуйте дизайн для нее), написанную кем-то, кто действительно знает архитектуру и компилятор.Вероятно, это будет правильно, а при условии правильности, вероятно, превзойдет все, что я изобрел сам.