Обязательно ли поле ссылки быть изменчивым, если я изменяю его во время блокировки? - PullRequest
2 голосов
/ 09 июля 2011

Рассмотрим следующий код, который выполняется в фоновом потоке («поток B»):

List<T> invocationQueueCopy;
lock (invocationQueue)
{
    invocationQueueCopy = invocationQueue;
    invocationQueue = new List<T>();
}

В другом потоке («поток A») я просто блокирую «invocationQueue» перед добавлением в него:

lock (invocationQueue)
{
    invocationQueue.Add(args);
}

Я читал, что присвоение ссылок является атомарным, но может когда-нибудь случиться так, что «поток А» в конечном итоге выполнит запись в старый список (тот, который был заменен в «потоке В») после получениязамок?Я читал другие ответы, которые подразумевают, что это могло бы быть, если бы значение ссылки хранилось в регистре в «потоке A», тогда он не знал бы, что «поток B» изменил значение в классе.Если это так, может ли объявление «invocationQueue» volatile предотвратить это?

Примечания:

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

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

Заранее спасибо.

Редактировать:

Просто чтобы уточнить из комментариев Адама: invocationQueue - это закрытое поле, которое создается внутри этого класса и никогда не подвергается воздействию внешнего мира, поэтому ничто не может заблокировать его, кроме этих двухметоды.

1 Ответ

4 голосов
/ 09 июля 2011

РЕДАКТИРОВАТЬ: Ваше решение будет работать.Блокировка создает полный забор , поэтому любое кэширование предотвращается, в основном это означает, что вы всегда получите самое последнее значение для ссылки на список.Единственное, что предлагается в комментариях, это то, что вы должны делать блокировку нейтрального объекта, а не самого списка.

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

Да, этоможет случиться, поэтому не делайте так.

Это не улучшится, даже если вы сделали блокировку только для чтения любого объекта.

Посмотрите, что может произойти (хотя большинствораз это не произойдет).

ThreadA и ThreadB выполняются на разных процессорах, каждый из которых имеет собственную кэш-память, которая содержит ссылку на incovationQueue.

  • ThreadB блокирует invocationQueue,блокировка выполняется для ссылки, которая берется для кэша процессора1, а не для имени переменной.
  • ThreadB копирует invocationQueue.
  • ThreadA блокирует invocationQueue, блокировка выполняется для ссылкикоторый берется для кеша на процессоре 2 и который в этот момент совпадает с кэшем на процессоре 1 и начинает ждать.
  • ThreadB создает новый список и назначает егодо invocationQueue, кэш в процессоре 1 обновляется, но поскольку переменная НЕ является изменчивой, это все, что происходит.
  • ThreadA входит в блокировку и получает ссылку из своего кэша, которая указывает на старую ссылку, поэтому вы в конечном итоге добавляете переменную в старый список.

Так что вам нужносделайте список изменчивым И используйте блокировку, если вы собираетесь играть с самой ссылкой.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...