Несколько потоков пишут одни и те же данные в буфер: есть ли согласованные аргументы в пользу того, что это опасно?

Представьте себе ситуацию, в которой несколько потоков могут писать в определенный буфер (без ограничений на его размер) и применяются все эти ограничения:

  1. Перед запуском всех потоков буфер инициализируется нулем.
  2. Поток может писать в буфер или нет.
  3. Если поток пишет в буфер, он записывает определенную строку байтов (назовем ее V, причем V состоит не только из нулей и одинакова для всех потоков).
  4. Ни один поток никогда не читал из буфера.
  5. То, что поток пишет в буфер, и то, что он пишет в буфер, не зависит от того, писали в буфер другие потоки или нет.
  6. Если поток начинает запись в буфер, то он записывает его полностью.
  7. Потоки пишут в буфер, не следуя точному порядку байтов.

Вопрос: после того, как все потоки остановились (и это происходит только при условии, что они закончили запись в буфер, если они начали), гарантируется ли мне при любой реальной или хотя бы правдоподобной архитектуре, что:

  1. либо буфер содержит все нули,
  2. или буфер содержит ровно V?

Если нет, то есть ли какой-либо последовательный аргумент, утверждающий, что может быть записана другая строка байтов, назовем ее V'? Если да, то в чем могут быть различия между V и V'? Почему?


person gd1    schedule 16.05.2013    source источник
comment
Вопрос не столько в том, содержит ли буфер 0 или V: он содержит все, что вы написали, - более интересный вопрос: если я прочитаю буфер, гарантированно ли я прочитаю 0 или V. Ответ на последний зависит от модели памяти вашей реальной или, по крайней мере, вероятной архитектуры, которая, в свою очередь, зависит от используемого вами языка/среды выполнения/процессора...   -  person assylias    schedule 17.05.2013
comment
Является ли V одинаковым для всех потоков?   -  person user207421    schedule 17.05.2013
comment
@assylias: пожалуйста, не могли бы вы опубликовать подробный ответ? Я хотел бы прочитать вашу точку зрения, которая отличается от точки зрения людей, которые уже ответили, - при условии, что ваша интерпретация вопроса такая же.   -  person gd1    schedule 17.05.2013
comment
@ gd1 Я недостаточно уверен в ответе, чтобы опубликовать его, но я чувствую, что если ваш буфер достаточно велик (и разделен между строками кеша), его обновление в / из основной памяти не будет атомарным, и поток чтения может прочитать частичное обновление из основной памяти, если видимость не обеспечивается соответствующим барьером памяти. Окно, в течение которого будет наблюдаться это частичное обновление, вероятно, очень короткое для большинства архитектур.   -  person assylias    schedule 17.05.2013
comment
@gd1 Где язык вступает в игру: если вы, например, читаете буфер из программы Java, и вы читаете частичное обновление, и вы не используете какую-либо синхронизацию, тогда JVM может поднять значение, и ваш поток может сохранить используя это частичное значение навсегда, поскольку оно не будет повторно читать общую переменную.   -  person assylias    schedule 17.05.2013
comment
Если написать это на C++, компилятор может и все испортит, если не сегодня, то в какой-нибудь будущей версии. Разрешается переписывать ваш код так, как ему заблагорассудится, пока результат совпадает с кодом, который вы написали, и при этом разрешено предполагать, что у вас нет гонок, поэтому, поскольку вы делаете есть гонки, все ставки выключены. Например, он может переписать обратный вызов вашего потока с кодом, который сначала записывает V в буфер, но сдвигает байт вверх, а затем сдвигает содержимое буфера вниз. Когда два потока делают это одновременно, результат может отличаться от V или 0.   -  person Don Hatch    schedule 20.03.2017
comment
@DonHatch: действительно компилятор может это сделать? Представьте, что есть один поток, но я получаю сигнал, нельзя ли в обработчике сигнала полагаться на то, что буфер будет либо заполнен нулями, либо какой-то частью V (может быть, частичной, но не перепутанной)? Конечно, я бы не осмелился сделать это в обработчике сигналов, но, думаю, вы поняли. Спасибо за разъяснение, и не стесняйтесь публиковать ответ, за который я могу проголосовать.   -  person gd1    schedule 20.03.2017
comment
@gd1 Я избегал публиковать ответ, потому что это потребовало бы правильного сбора ссылок :-) Но, например, посмотрите на них, чтобы открыть глаза на сумасшедшие вещи, которые компилятор C++ может и делает: blog.mozilla.org/nnethercote/2015/02/24/ и software.intel.com/en-us/blogs/2013/01/06/   -  person Don Hatch    schedule 20.03.2017


Ответы (2)


Номер 6 кажется решающим, он в основном утверждает, что запись в буфер является атомарной и непрерываемой. Следовательно, содержимое будет либо 0, либо последовательное v.

Конечно, это не обычный случай с потоками, если вы не используете что-то вроде мьютекса.

person paxdiablo    schedule 16.05.2013

Да, это гарантировано (если все потоки действительно записывают одни и те же значения в одни и те же места). Хранилища байтов являются атомарными. Это означает, что никакие значения не могут быть "придуманы" системой. Единственными возможными значениями для каждого байта в буфере являются либо ноль (поскольку он был предварительно инициализирован), либо значение, которое записывают все потоки.

Вопрос теперь в следующем: может ли ноль проскальзывать хоть как-то? Может ли место в byte[] каким-то образом оказаться так, что оно никогда не было записано? Ответ: нет. Неясно, какая запись приходит, но они все одинаковые. Если хотя бы один поток записал в буфер, он будет содержать ровно V. (Если никто никогда не писал, это будут все нули. Это тривиально. Поэтому я предполагаю, что по крайней мере один поток записал в буфер).

Конечно, ваш поток чтения должен дождаться завершения работы писателей, чтобы установить барьер памяти. Барьер гарантирует, что все предыдущие записи будут видны читателю.

person usr    schedule 16.05.2013