.NET Threading - блокировка, необходимая для назначений - PullRequest
12 голосов
/ 23 февраля 2011

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

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

private IList<ServerStatus> status;

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

status = GetUpdatedStatus();

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

var currentStatus = status;

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

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

Ответы [ 2 ]

20 голосов
/ 23 февраля 2011

Вы правы.Вы увидите задание или не увидите его.Назначения (и чтения) ссылок всегда являются «атомарными» (в конце концов, это потому, что на 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

Ах ...Я забыл ... Мир ненавидит тебя, поэтому есть кое-что, что нужно знать об изменчивости: иногда это не работает :-) :-) Прочтите, на той же странице другого примера, раздел 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) ИЛИ заполнить вашу программу потоком.MemoryBarrier (но не пытайтесь, у вас ничего не получится, и вы даже не узнаете почему).Вам гарантировано, что все чтения и записи, сделанные в блокировке, будут выполняться В блокировке, а не до или после.

6 голосов
/ 23 февраля 2011

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

  • использование в узком цикле - может потребоваться добавить 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));
...