.NET Threading - блокировка, необходимая для назначений

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

У меня есть член поля:

private IList<ServerStatus> status;

Он обновляется в виде потока:

status = GetUpdatedStatus();

И он используется в другом потоке, как это:

var currentStatus = status;

Итак, вопрос в том, может ли вышеупомянутое вызывать какие-либо проблемы без блокировок вокруг двух операторов присваивания?

Я предполагаю, что единственный сценарий, который я вижу, - currentStatus, являющийся нулевым, но опять же я ожидаю, что присвоение будет несколько потокобезопасным (либо оно изменило ссылку, либо нет)

Ответ 1

Вы правы. Вы увидите задание, или вы его не увидите. Задания (и чтения) ссылок всегда "атомарны" (в конце концов, потому что на 32-битных машинах ссылки имеют 32 бита, поэтому их можно выполнять атомарно, а на 64-битных машинах (с 64-разрядным приложением) ссылки - 64 бита, так что это может быть сделано атомарно. Единственное исключение - это попытка написать/прочитать длинный (64 бит) на 32-битной машине. Там вам придется использовать Interlocked.Read/Interlocked.Exchange)

Обычно должен объявлять статус volatile, так что каждый поток видит только последнюю версию. Вы должны прочитать следующее: http://www.albahari.com/threading/ это очень хорошо!

Если вы мне не доверяете, прочитайте раздел Do We Really Need Locks and Barriers? здесь http://www.albahari.com/threading/part4.aspx

Ах... Я забыл... Мир HATES вы, так что есть немного, чтобы знать об изменчивости: иногда это не работает:-):-) Читайте на той же странице другой например, раздел The volatile keyword, часть под красным полем. Notice that applying volatile doesn’t prevent a write followed by a read from being swapped, and this can create brainteasers. В конце концов, единственный способ быть уверенным в том, чтобы использовать Interlocked.Exchange для записи и Interlocked.CompareExchange для чтения чего-либо или защитить разделы чтения и записи с синхронизацией (например, lock) ИЛИ заполнить вашу программу Thread.MemoryBarrier( но не пытайтесь, вы потерпите неудачу, и вы даже не узнаете почему). Вам гарантировано, что все чтения и записи, сделанные в блокировке, будут выполнены в блокировке, а не до или после.

Ответ 2

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

  • использование в узком цикле - возможно, нужно добавить volatile, если вам нужно заметить изменение
  • двойные обновления; если вы (например) делаете добавление/удаление посредством ссылочного свопа, вы должны использовать Interlocked.CompareExchange, чтобы убедиться, что вы не потеряете данные; продолжайте повторное внесение изменений, пока не выиграете обмен.

то есть.

object snapshot, newValue;
do
{
    snapshot = field;

    // do something based on that; create a clone
    // with more/less data for example
    newValue = ...;
} while (!ReferenceEquals(
    Interlocked.CompareExchange(ref field, newValue, snapshot), snapshot));