Многопоточность .NET - Нужно ли синхронизировать доступ к переменной примитивного типа? - PullRequest
7 голосов
/ 11 августа 2009

Сценарий

У меня есть класс со свойством bool Enabled, который используется циклом в другом потоке, чтобы увидеть, должен ли он остановиться или нет. Идея состоит в том, что другой поток может установить для этого свойства значение false и остановить работу другого потока без ошибок.

Вопрос

Стоит ли мне сериализовать доступ к этому свойству Enabled, используя что-то вроде lock (lockObject) { ... }, или это безопасно без?

Ответы [ 9 ]

6 голосов
/ 11 августа 2009

Чтения примитивного типа являются атомарными при условии, что они вписываются в чтение ЦП. Таким образом, чтение 32-битного примитивного типа является атомарным на 32-битном процессоре, тогда как чтение 64-битного типа - нет. Однако, если это не volatile, ваш другой поток может не увидеть изменений из-за кэширования.

3 голосов
/ 11 августа 2009

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

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

Соответствующая часть спецификации языка C # ( 10.4.3 Volatile fields ) далее гласит:

Для изменчивых полей такое переупорядочение оптимизации ограничены:

  • Чтение изменчивого поля называется изменчивым чтением. Летучий чтение имеет "приобрести семантику"; то есть, это гарантированно произойдет до любого ссылки на память, которые происходят после это в последовательности инструкций.
  • Запись изменчивого поля называется изменчивой записью. Летучий write имеет "семантику релиза"; тот это гарантированно произойдет после любые ссылки на память до написать инструкцию в инструкции последовательность.

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

  • Тип ссылки.
  • Тип byte, sbyte, short, ushort, int, uint, char, float или bool.
  • enum -тип с базовым типом перечисления byte, sbyte, short, ushort, int или uint.
3 голосов
/ 11 августа 2009

Если один поток читает значение, а другой поток записывает значение, в этом случае это безопасно, но обратите внимание на объявление свойства Enabled с ключевым словом volatile, чтобы убедиться, что значение будет синхронизировано со всеми процессорами, если вы запустите с двухъядерным ПК.

3 голосов
/ 11 августа 2009

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

1 голос
/ 11 августа 2009

Если вы не пометили его как энергозависимый, он будет работать на компьютерах с одним процессором (из-за 32-битной атомарности), но не на компьютерах с несколькими процессорами, так как он будет хранить значение в своем встроенном кэше и не будет извлекать последнее значение, следовательно, вам нужно пометить с помощью Volatile. Это правда, что вам не нужно блокировать элементы, если их размер 32 бита, это просто изменение размера реестра и, следовательно, атомарное.

Здесь есть статья: www.yoda.arachsys.com / csharp / threads / volatility.shtml

Существует также нечто, называемое Домены синхронизации .

Домен синхронизации обеспечивает автоматическую синхронизацию потокового доступа к объектам декларативно. Этот класс был представлен как часть инфраструктуры, поддерживающей удаленное взаимодействие .NET. Разработчики, желающие указать, что класс должен иметь доступ к своим синхронизированным объектам, должны иметь класс, унаследованный от ContextBoundObject, и пометить его атрибутом SynchronizationAttribute следующим образом:

[Synchronization]
public class MyController : ContextBoundObject 
{
  /// All access to objects of this type will be intercepted
  /// and a check will be performed that no other threads
  /// are currently in this object's synchronization domain.
}

Дополнительную информацию о доменах синхронизации можно найти здесь: msdn.microsoft.com / en-us / magazine / dd569749.aspx

1 голос
/ 11 августа 2009

Если вы не используете volatile или блокируете логическое значение, оно будет работать на x86. Он может не работать на процессорах Intel Itanium (IA64). Вот хорошая статья codeproject kb , которая объясняет все это.

0 голосов
/ 11 августа 2009

Вы можете, как уже написанные люди, пометить переменную volatile или lock it.

Однако то, что вы пытаетесь выполнить (позволяя потокам связываться друг с другом посредством сигнализации), уже встроено в .NET Framework.

Взгляните на ManualResetEvent на MSDN .

0 голосов
/ 11 августа 2009

Существует пример MSDN , который точно описывает, что вы пытаетесь сделать: Как: создавать и завершать потоки (Руководство по программированию в C #) . Он предполагает, что вам нужно объявить свойство Enabled как volatile и не нужно блокировать it.

0 голосов
/ 11 августа 2009

Вы можете использовать Interlocked.Exchange в этом случае, если вас беспокоит уродливая блокировка.

...