Атомарность оператора объединения C # - PullRequest
7 голосов
/ 23 февраля 2012

Сегодня я натолкнулся на какой-то одноэлементный код в нашей кодовой базе, и я не был уверен, что это потокобезопасное:*Я полагаю, что ??это просто трюк с компилятором, и что полученный код все еще НЕ атомарный.Другими словами, два или более потоков могут найти _sentence равным нулю, прежде чем устанавливать _sentence для нового предложения и возвращать его.

Чтобы гарантировать атомарность, нам нужно заблокировать этот бит кода:

public static IContentStructure Sentence{ 
    get {

       lock (_sentence) { return _sentence ?? (_sentence = new Sentence()); }
    }
}

Это все правильно?

Ответы [ 3 ]

16 голосов
/ 24 февраля 2012

Сегодня я столкнулся с каким-то одноэлементным кодом в нашей кодовой базе

У вас есть такой запутанный код по всей вашей кодовой базе? Этот код делает то же самое:

if (_s == null) 
    _s = new S();
return _s;

и читается в тысячу раз легче.

Я верю этому ?? это просто трюк с компилятором, и что полученный код все еще НЕ атомарный

Вы правы. C # дает следующие гарантии атомарности:

Чтение и запись следующих типов данных являются атомарными: bool, char, byte, sbyte, short, ushort, uint, int, float и reference. Кроме того, чтение и запись перечислимых типов с базовым типом в предыдущем списке также являются атомарными. Чтение и запись других типов, включая long, ulong, double и decimal, а также определяемые пользователем типы, не гарантированно являются атомарными. Помимо библиотечных функций, разработанных для этой цели, нет гарантии атомарного чтения-изменения-записи, например, в случае увеличения или уменьшения.

Нулевой оператор объединения не входит в этот список гарантий.

Чтобы гарантировать атомарность, нам нужно заблокировать этот бит кода:

lock (_sentence) { return _sentence ?? (_sentence = new Sentence()); } } }    

Боже мой, нет. Это сразу падает!

Правильно сделать одно из:

  • Перестаньте пытаться писать многопоточный код.
  • Напишите синглтон, используя один из безопасных документов Джона Скита на своей странице о синглетонах.
  • Используйте класс Lazy<T>.
  • Блокировка объекта, предназначенного для блокировки этой переменной.
  • Используйте Interlocked Compare Exchange для выполнения атомарного теста и установки.
12 голосов
/ 23 февраля 2012

Вы правы;это совсем не потокобезопасно.

1 голос
/ 24 февраля 2012

Вы можете использовать Interlocked.CompareExchange с null для получения операции ??, которая является атомарной.

// I made up my own Sentence type
Sentence current = null;
var whenNull = new Sentence() {Text = "Hello World!"};

var original = Interlocked.CompareExchange(ref current, new Sentence() { Text = "Hello World!" }, null);

Assert.AreEqual(whenNull.Text, current.Text);
Assert.IsNull(orig);

// try that it won't override when not null
current.Text += "!";
orig = Interlocked.CompareExchange(ref current, new Sentence() { Text = "Hello World!" }, null);

Assert.AreEqual("Hello World!!", current.Text);
Assert.IsNotNull(orig);
...