Подтвердить что ты не робот

Гарантировать, что актуальное значение переменной всегда отображается для нескольких потоков в многопроцессорной системе

Я использую такую ​​конфигурацию:

  • .NET framework 4.5
  • Windows Server 2008 R2
  • HP DL360p Gen8 (2 * Xeon E5-2640, x64)

У меня есть такое поле где-то в моей программе:

protected int HedgeVolume;

Я обращаюсь к этому полю из нескольких потоков. Я предполагаю, что, поскольку у меня многопроцессорная система, возможно, что эти потоки выполняются на разных процессорах.

Что мне делать, чтобы гарантировать, что в любое время, когда я использую это поле, самое последнее значение "прочитано"? И чтобы убедиться, что когда я "напишу" значение, он станет доступен для всех других потоков немедленно?

Что мне делать?

  • просто оставить поле как есть.
  • объявить его volatile
  • использовать класс Interlocked для доступа к полю
  • используйте методы .NET 4.5 Volatile.Read, Volatile.Write для доступа к полю.
  • использовать lock

Мне нужен только самый простой способ заставить мою программу работать в этой конфигурации. Мне не нужна моя программа для работы на других компьютерах или серверах или операционных системах. Также я хочу минимальную латентность, поэтому я ищу решение быстрее, которое всегда будет работать с стандартной конфигурацией этой (многопроцессорный intel x64,.net 4.5).

4b9b3361

Ответ 1

В вашем вопросе отсутствует один ключевой элемент... Насколько важна целостность данных в этом поле?

volatile дает вам производительность, но если поток в настоящее время записывает изменения в поле, вы не получите эти данные до тех пор, пока они не будут выполнены, поэтому вы можете получить доступ к устаревшей информации и, возможно, перезаписать изменения, делает. Если данные чувствительны, вы можете получить ошибки, которые будут очень трудно отслеживать. Однако, если вы делаете очень быстрое обновление, перезапишите это значение, не читая его, и не заботясь о том, чтобы время от времени вы устарели (на несколько миллисекунд) данных, идите за ним.

lock гарантирует, что только один поток может получить доступ к полю за раз. Вы можете поместить его только на методы, которые пишут поле и оставляют метод чтения один. Нижняя сторона - медленная и может блокировать поток, а другая выполняет свою задачу. Однако вы уверены, что ваши данные остаются в силе.

Interlock существуют, чтобы защитить себя от переключателя контекста планировщика. Мое мнение? Не используйте его, если вы точно не знаете, почему вы его используете, и как его использовать. Он дает варианты, но с отличными вариантами очень проблематично. Он предотвращает переключение контекста во время обновления переменной. Возможно, это не так, как вы думаете, и не будет препятствовать параллельным потокам выполнять свои задачи одновременно.

Ответ 2

Вы хотите использовать Volatile.Read().

Когда вы работаете на x86, все записи на С# эквивалентны Volatile.Write(), вам нужно использовать это только для Itanium.

Volatile.Read() будет гарантировать, что вы получите последнюю копию независимо от того, какой поток последний раз написал ее.

Здесь есть фантастическая запись, Объяснение модели памяти С#

Резюме включает,

На некоторых процессорах необходимо не только избежать компилятора оптимизации волатильных чтений и записи, он также должен использовать специальные инструкции. На многоядерной машине разные ядра имеют разные кэши. Процессоры могут не потрудиться, чтобы эти кеши были согласованы по умолчанию и специальные инструкции могут потребоваться для очистки и обновления кэш.

Надеюсь, что многое очевидно, кроме необходимости volatile, чтобы остановить компилятор от его оптимизации, есть и процессор.

Однако в С# все записи являются волатильными (в отличие от Java) независимо от того, записываете ли вы в энергозависимое или энергонезависимое поле. Таким образом, вышеприведенная ситуация на С# никогда не происходит. Неустойчивая запись обновляет кеш потоков, а затем очищает весь кеш до основного память.

Вам не нужно Volatile.Write(). Более авторитарный источник здесь, Модель памяти CLR Джо Даффи. Однако вам может потребоваться его, чтобы остановить компилятор, переупорядочивающий его.

Так как все записи С# являются неустойчивыми, вы можете думать обо всех прямо в основную память. Регулярное, неизменяемое чтение может читать значение из кеша потоков, а не из основного

Вам нужно Volatile.Read()

Ответ 3

Когда вы начинаете разработку параллельной программы, вы должны рассмотреть эти параметры в порядке предпочтения:

1) Изоляция: каждый поток имеет собственные личные данные
2) Неизменяемость: потоки могут видеть общее состояние, но оно никогда не меняется

3) Mutable shared state: защита всего доступа к общему состоянию с помощью блокировок

Если вы доберетесь до (3), то насколько быстро вам это действительно нужно?

Приобретение незапрошенной блокировки занимает порядка 10ns (10 -8 секунд) - это достаточно быстро для большинства приложений и является самым простым способом гарантировать правильность.

Используя любой из других опций, которые вы упомянули, вы попадаете в область программирования с низким уровнем блокировки, что безумно сложно получить.


Если вы хотите узнать, как писать параллельное программное обеспечение, вы должны прочитать следующее:

Вступление: Бесплатная электронная книга Джо Альбахари - займет около дня, чтобы прочитать

Библия: Джо Даффи "Параллельное программирование в Windows" - потребуется около месяца для чтения

Ответ 4

Зависит от того, что вы делаете. Для чтения только волатильность проще всего, блокировка позволяет немного больше контролировать. Блокировка не нужна, так как она более густая, чем проблема, которую вы описываете. Не уверен в Volatile.Read/Write, никогда не использовал их.

Ответ 5

volatile - плохо, есть некоторые проблемы (см. блог Джо Даффи)

если все, что вы делаете, читает значение или безоговорочно записывает значение - используйте Volatile.Read и Volatile.Write

если вам нужно прочитать и впоследствии написать обновленное значение - используйте синтаксис lock. Однако вы можете добиться такого же эффекта без блокировки с помощью функциональных возможностей Interlocked, но это сложнее (включает CompareExchange, чтобы убедиться, что вы обновляете значение чтения, т.е. не было изменено, так как операция чтения + логика повторяется, если значение было изменено после чтения).

Ответ 6

Из этого я могу понять, что вы хотите иметь возможность прочитать последнее значение, которое оно было рукописным в поле. Давайте сделаем аналогию с проблемой sql-совместимости данных. Если вы хотите прочитать последнее значение поля, вы должны сделать атомарные инструкции. Если кто-то пишет поле, все потоки должны быть заблокированы для чтения, пока этот поток не завершит транзакцию записи. После этого каждый, прочитанный в этом потоке, будет в безопасности. Проблема заключается не в чтении, а в написании. Блокировка на этом поле всякий раз, когда его хватка должна быть достаточной, если вы спросите меня...

Ответ 7

Сначала посмотрите здесь: Нестабильный против блокировки против блокировки

Неустойчивый модификатор shurely является хорошим вариантом для многоядерного процессора.

Но этого достаточно? Это зависит от того, как вы вычисляете новое значение HedgeVolume!

  • Если ваш новый HedgeVolume не зависит от текущего HedgeVolume, тогда ваш сделанный с изменчивым.

  • Но если HedgeVolume [x] = f (HedgeVolume [x-1]), вам нужна некоторая синхронизация потоков, чтобы гарантировать, что HedgeVolume не изменится при вычислении и назначении нового значения. В этом случае подходят как блокировки, так и блокированные szenarios.