Запись в ссылочных типов (т.е. Object
) и типов слов размера значений (т.е. int
в битовой системе 32) являются атомарными. Это означает, что когда вы заглядываете в значения (позиция 6), вы можете быть уверены, что либо получите старое значение, либо новое значение, но не что-то другое (если у вас есть такой тип, как большая структура, которую он может сращивать, и вы можете прочитать значение, когда оно было на полпути, написанное). Вам не нужно a lock
или volatile
, если вы готовы принять потенциальный риск чтения устаревших значений.
Обратите внимание, что поскольку в этот момент не введен барьер памяти (lock
или использование volatile
, добавьте один), возможно, что переменная была обновлена в другом потоке, но текущий поток не соблюдает это изменение ; он может читать «устаревшее» значение (потенциально) через некоторое время после его изменения в другом потоке. Использование volatile
гарантирует, что текущий поток может наблюдать изменения в переменной раньше.
Вы можете быть уверены, что после звонка на номер WaitAll
вы получите соответствующее значение, даже без lock
или volatile
.
Также обратите внимание, что, хотя вы можете быть уверены, что ссылка на ссылочный тип написана атомарно, ваша программа не дает никаких гарантий относительно наблюдаемого порядка любых изменений фактического объекта, на который ссылается ссылка. Даже если с точки зрения фонового потока объект инициализируется до того, как он будет назначен полю экземпляра, это может не произойти в этом порядке. Таким образом, другой поток может наблюдать запись ссылочного объекта, но затем следовать этой ссылке и находить объект в инициализационном или частично инициализированном состоянии. Введение барьера памяти (т. Е. С помощью переменной volatile
может потенциально позволить вам избежать выполнения таких переупорядочений, тем самым гарантируя, что этого не произойдет. Вот почему это better to just not do this in the first place и просто вернуть две задачи результаты, которые они генерируют, а не манипулируют закрытой переменной.
WaitAll
будет вводить барьер памяти в дополнение к обеспечению того, что две задачи фактически завершены, что означает, что вы знаете, что переменные являются актуальными и не будут иметь устаревших значений
Ответ Сервита правильный, но будьте осторожны с вашими ожиданиями в позиции G, так как вы можете получить некоторые неинтуитивные результаты. Например, вы можете 'o' назначить новый объект, но' _o' все еще является нулевым, несмотря на то, что они должны были произойти в обратном порядке. – Douglas
Кроме того, если целью вашего «заглядания» является обработка результатов завершенной задачи до того, как все остальные также будут завершены, тогда правильный путь - заменить ваш 'WaitAll' на цикл вызовов' WaitAny' , – Douglas