назначение ссылки является атомарным, поэтому почему блокируется.Обмен (ref объект, объект) необходим?
в моем многопоточном веб-сервисе asmx у меня было поле класса _allData моего собственного типа SystemData, которое состоит из нескольких List<T>
и Dictionary<T>
помечен как volatile
. Системные данные (_allData
) обновляется один раз в то время, и я делаю это, создавая другой объект под названием newData
и заполнить его структуры данных с новыми данными. Когда это сделано, я просто назначаю
private static volatile SystemData _allData
public static bool LoadAllSystemData()
{
SystemData newData = new SystemData();
/* fill newData with up-to-date data*/
...
_allData = newData.
}
это должно работать, так как назначение является атомарным и потоки, которые имеют ссылку на старые данные продолжают использовать его а остальные имеют новые системные данные сразу после назначения. Однако мой коллега сказал, что вместо использования volatile
ключевое слово и простое назначение Я должен использовать InterLocked.Exchange
потому что он сказал, что на некоторых платформах не гарантируется, что ссылка цессии является атомарным. Более того: когда я объявляю
4 ответа:
здесь есть много вопросов. Рассматривая их по одному:
назначение ссылки является атомарным, так почему блокируется.Обмен (ref объект, объект) необходим?
назначение ссылки является атомарным. Сблокированный.Exchange выполняет не только назначение ссылок. Он выполняет чтение текущего значения переменной, прячет старое значение и присваивает новое значение переменной, все как атомарная операция.
мой коллега сказал, что на некоторых платформах не гарантируется, что назначение ссылок является атомарным. Был ли мой коллега прав?
нет. Назначение ссылок гарантированно будет атомарным на всех платформах .NET.
мой коллега, исходя из ложных предпосылок. Значит ли это, что их выводы неверны?
Не обязательно. Ваш коллега может дать вам хороший совет по плохим причинам. Возможно, есть какая-то другая причина вы должны использовать блокировку.Обмен. Программирование без блокировки безумно сложно, и в тот момент, когда вы отходите от устоявшихся практик, поддерживаемых экспертами в этой области, вы уходите в сорняки и рискуете худшими условиями гонки. Я не являюсь ни экспертом в этой области, ни экспертом по вашему коду, поэтому я не могу судить так или иначе.
выдает предупреждение "ссылка на летучее поле не будет рассматриваться как летучая" что я должен думать насчет этого?
вы должны понять, почему это проблема в целом. Это приведет к пониманию того, почему предупреждение не имеет значения в данном конкретном случае.
причина, по которой компилятор дает это предупреждение, заключается в том, что пометка поля как volatile означает, что "это поле будет обновляться в нескольких потоках-не создавайте код, который кэширует значения этого поля, и убедитесь, что любые чтения или записи этого поля не перемещаются вперед и назад во времени " через несоответствия кэша процессора."
(Я предполагаю, что вы уже все это понимаете. Если у вас нет подробного понимания значения volatile и того, как это влияет на семантику кэша процессора, вы не понимаете, как это работает и не должны использовать volatile. Программы Без блокировки очень трудно получить правильно; убедитесь, что ваша программа права, потому что вы понимаете, как это работает, а не случайно.)
теперь предположим, что вы сделайте переменную, которая является псевдонимом изменчивого поля, передав ссылку на это поле. Внутри вызываемого метода компилятор не имеет никаких оснований знать, что ссылка должна иметь изменчивую семантику! Компилятор будет бодро генерировать код для метода, который не может реализовать правила для изменчивых полей, но переменная и поля volatile. Это может полностью разрушить вашу логику без блокировки; предположение всегда заключается в том, что летучее поле всегда обращались с volatile семантика. Нет смысла рассматривать его как изменчивый иногда, а не в других случаях; вы должны всегда будьте последовательны в противном случае вы не можете гарантировать согласованность на других доступах.
поэтому компилятор предупреждает, когда вы это делаете, потому что он, вероятно, полностью испортит вашу тщательно разработанную логику без блокировки.
конечно, блокировать.Обмен и написано ожидать a летучие поля и делать правильные вещи. Поэтому предупреждение вводит в заблуждение. Я очень сожалею об этом; то, что мы должны были сделать, - это реализовать какой-то механизм, посредством которого автор такого метода, как Interlocked.Exchange может поместить атрибут в метод, говоря: "этот метод, который принимает ref, применяет изменчивую семантику к переменной, поэтому подавляет предупреждение". Возможно, в будущей версии компилятора мы так и сделаем.
либо ваш коллега ошибается, либо он знает что-то, что спецификация языка C# не делает.
5.5 атомарность переменных ссылок:
" читает и записывает следующее типы данных являются атомарными: bool, char, byte, sbyte, short, ushort, uint, int, плавающие и ссылочные типы."
таким образом, вы можете написать в volatile ссылку без риска получения поврежденного значения.
вы должны конечно, будьте осторожны с тем, как вы решаете, какой поток должен получать новые данные, чтобы свести к минимуму риск того, что это сделает более одного потока за раз.
устанавливает переменную указанного типа T в указанное значение и возвращает исходное значение, как атомарную операцию.
Он изменяет и возвращает исходное значение, это бесполезно, потому что вы только хотите изменить его, и, как сказал Гуффа, он уже атомарный.
Если профилировщик не доказал, что он является узким местом в вашем приложении, вы должны рассмотреть возможность снятия блокировок, это проще поймите и докажите, что ваш код является правильным.
Iterlocked.Exchange()
не просто атомарный, он также заботится о видимости памяти:следующие функции синхронизации используют соответствующие барьеры для обеспечения упорядочения памяти:
функции, которые входят или выходят из критических разделов
функции, которые сигнализируют объекты синхронизации
Подожди функции
блокировать функции
синхронизация и мультипроцессор Проблемы
Это означает, что в дополнение к атомарность гарантирует, что:
- для потока, вызывающего его:
- переупорядочение инструкций не выполняется (компилятором, временем выполнения или аппаратным обеспечением).
- для всех потоков:
- нет чтения в память, которые происходят до того, как эта инструкция увидит изменение этой инструкции.
- все читает после этой инструкции, увидите изменение, внесенное настоящей инструкцией.
- все записи в память после того, как эта инструкция произойдет после того, как это изменение инструкции достигло основной памяти (путем промывки этого изменения инструкции в основную память, когда это сделано, и не позволяйте аппаратному Флешу его владеть своим временем).