Являются ли операции чтения и записи свойств атомарными в C#?

Чтение и запись в некоторые примитивные типы в C#, такие как bool и int, являются атомарными.

(См. раздел 5.5, «5.5 Атомарность ссылок на переменные» в Спецификации языка C#.)

Но как насчет доступа к таким переменным через свойства? Разумно ли предположить, что они также будут атомарными и потокобезопасными? Например. Является ли чтение MyProperty ниже атомарным и потокобезопасным?:

public bool MyProperty { get { return _foo; } }

А как насчет автоматически реализуемых свойств?

public bool MyProperty { get; }

person dan-gph    schedule 20.07.2009    source источник
comment
Я сам давно хотел спросить об этом ... Я просто предполагаю, поэтому не буду давать ответ, но меня не удивит, если авто-свойства будут атомарными. Что-нибудь еще, вероятно, не все же.   -  person Matthew Scharley    schedule 20.07.2009


Ответы (6)


Вам нужно более четко различать «атомарные» и «поточно-ориентированные». Как вы сказали, записи являются атомарными для большинства встроенных типов значений и ссылок.

Однако это не означает, что они потокобезопасны. Это просто означает, что если оба значения "A" и "B" записаны, поток никогда не увидит что-то среднее между ними. (например, изменение с 1 на 4 никогда не покажет 5, или 2, или любое другое значение, кроме 1 или 4.) Это не означает, что один поток увидит значение "B", как только это было записано в переменную. Для этого вам нужно взглянуть на модель памяти с точки зрения волатильности. Без барьеров памяти, обычно получаемых с помощью блокировок и/или изменчивых переменных, запись в основную память может быть отложена, а чтение может быть ускорено, фактически предполагая, что значение не изменилось с момента последнего чтения.

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

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

person Jon Skeet    schedule 20.07.2009
comment
Спасибо. Я предположил, что атомарный означает атомарный везде, в том числе между потоками. Вот что мне подсказало это слово. Стрижка действительно таит в себе опасность. - person dan-gph; 20.07.2009

Я немного не понимаю, о чем вы здесь спрашиваете. Кажется, вы могли бы задать 1 из 2 вопросов

  1. Является ли чтение _foo при вызове MyProperty атомарным?
  2. Установлено ли возвращаемое значение MyProperty атомарно?

Для № 1 ответ - да. Как указано в спецификации языка C# (и CLI), чтение и запись переменных определенных указанных типов гарантированно атомарны. Тип 'bool' является одним из таких типов.

Что касается № 2, лучше всего искать его в разделе 12.6.6 спецификации CLI. В нем говорится, что

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

Учитывая, что для использования возвращаемого значения MyProperty вы должны либо читать, либо записывать значение, можно с уверенностью предположить, что оно установлено атомарно.

person JaredPar    schedule 20.07.2009

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

Для потокобезопасности вам нужно использовать блокировку (или volatile, но об этом сложнее рассуждать). В свойствах нет ничего особенного — их тоже нужно сделать явно потокобезопасными.

person Jon    schedule 20.07.2009

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

Кроме того, если вы планируете получать доступ к значениям int/bool из нескольких потоков, вам следует пометить его (поле) как изменчивый. Это в основном предотвращает проблемы многопоточности, связанные с регистрами ЦП. (Это дополнение к блокировке, а не альтернатива)

person Richard Szalay    schedule 20.07.2009
comment
Это не правильно. Вы путаете атомарность памяти с видимостью среди потоков. Все операции чтения/записи переменных с размером меньше собственного int гарантированно являются атомарными. Дополнительные сведения см. в разделе 12.6.6 спецификации языка CLI. - person JaredPar; 20.07.2009
comment
Вы правы - я имел в виду потокобезопасный (не атомарный). Поделом со мной, что отвечаю так рано утром ;) - person Richard Szalay; 20.07.2009
comment
@Richard, утром я часто держусь подальше от SO, пока не допью кофе для собственной безопасности. Или, по крайней мере, мои представители :) - person JaredPar; 20.07.2009

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

person Jonathan Schuster    schedule 20.07.2009

Вы можете поместить что угодно в свойство, например

    Public Property foo() As Boolean
        Get
           goToLunch()
           barbecueRibs()
           return m_foo
        End Get
        Set(ByVal value As Boolean)
           takeANap()
           accessDatabase()
           messUpOtherVariables()
           m_foo = value
        End Set
    End Property
person Larry Watanabe    schedule 20.07.2009