Я чувствую, что на этой странице есть некоторая путаница.Во-первых, комментатор прав, что вопрос содержит опасное предположение:
int i = 5;
Interlocked.CompareExchange(ref i, 10, 5);
После этой команды int i будет иметь значение = 10 .
Нет, только если значение i
за это время не изменилось на значение, отличное от 5
.Хотя это кажется невероятным в показанном здесь коде, весь смысл использования CompareExchange
состоит в том, что это должно быть возможно, поэтому здесь важна техническая поддержка.Я беспокоюсь, что ОП может не понимать цели Interlocked.CompareExchange
, особенно потому, что он не проверяет возвращаемое значение (см. Ниже).
Теперь текст исходного вопроса был:
«Есть ли способ сделать это? Я хочу сравнить два экземпляра класса и присвоить одному из них значение, основанное на сравнении.»
Поскольку для слова нет жизнеспособного антецедента »это ", мы, возможно, должны рассмотреть вопрос здесь, предложение, которое следует после, давая перефразирование:
" Есть ли способ сравнить два экземпляра класса и присвоить одному из них значение, основанное насравнение? "
К сожалению, этот вопрос до сих пор неясен или, возможно, имеет мало общего с атомными операциями.Во-первых, вы не можете «назначить [экземпляр класса] значение».Это просто не имеет смысла. ссылка на экземпляр класса является значением, но нет никакого способа "присвоить" что-либо самому экземпляру класса.Это большая разница по сравнению с типами значений , которые могут назначаться друг другу.Вы можете создать экземпляр, используя оператор new
, но вы все равно просто получите ссылку на него.Опять же, это может показаться техническими деталями, но они являются критическими моментами, если вопрос действительно касается параллелизма без блокировки .
Далее, функция Interlocked.CompareExchange
необусловить место хранения значением , а точнее условно сохраняет значение в (заданном) месте , что означает, что оно либо сохраняет значение (успех), либо оставляет место хранения без изменений (сбой)), при этом достоверно указывая, какое из них имело место.
Это означает, что фраза «на основе сравнения» является неполной относительно того, какими должны быть альтернативные действия.Глядя на предыдущую часть вопроса ОП, можно предположить, что вопрос состоит в том, чтобы условно манипулировать ссылками на экземпляры, а атомарность - это красная сельдь.Трудно понять, потому что, как отмечалось выше, CompareExchange
(который использовался для постановки вопроса) не «подменяет» два значения в памяти, а только «сохраняет» одно значение.
X a = new X(1);
X b = new X(1);
X c = new X(2);
if (a.y == b.y)
a = c;
else
// ???
С перегрузкой Equals
это можно было бы упростить:
if (a == b)
a = c;
else
// ???
Внимание ФП к равенству внутреннего поля y
, кажется, увеличивает вероятность того, что эта интерпретация вопроса находится на правильном пути,Но очевидно, что ответы в этом направлении не имеют ничего общего с Interlocked.CompareExchange
.Нам нужно больше информации, чтобы понять, почему OP считает, что присвоение должно быть атомарным.
Поэтому в качестве альтернативы мы должны отметить, что также возможно атомарно поменять значения y
в существующих экземплярах:
var Hmmmm = Interlocked.CompareExchange(ref a.y, c.y, b.y);
Или экземпляр подкачки ссылок , и теперь должно быть очевидно, что приравнивающие ссылки определяются только в терминах "равенства ссылок":
var Hmmmm = Interlocked.CompareExchange(ref a, c, b);
Чтобы перейти отсюда, вопрос должен быть более ясным.Например, чтобы переформулировать комментарий, сделанный в другом месте на этой странице, но более строго, , было бы ошибкой не проверять возвращаемое значение Interlocked.CompareExchange .
Вот почему я сохранил возвращаемое значение в приведенном выше примере, и как я посчитал его имя подходящим. Не переходить на возвращаемое значение - значит не понимать базовых принципов безблокировочного («оптимистичного») параллелизма , обсуждение которого выходит за рамки этого вопроса. Отличное введение см. В Параллельное программирование в Windows Джо Даффи.
Наконец, я думаю, что весьма маловероятно, что OP действительно нужно атомарно хранить ссылки на классы, основываясь на произвольных соображениях, потому что это чрезвычайно специализированная операция, которая, как правило, необходима только в самой сути всеобъемлющего проектирования систем без блокировок. Но (вопреки другому ответу) это, безусловно, возможно в соответствии с тем, что описывает @supercat.
Поэтому, пожалуйста, не создавайте впечатление, что вы не можете писать код без блокировки в .NET или что ссылки на классы представляют собой какую-либо проблему для операций Interlocked
; на самом деле все обстоит с точностью до наоборот: если вам действительно нужно выполнить элементарную операцию, которая выбирает одно из двух разных мест хранения или иным образом влияет на несколько ячеек памяти, просто использовать схему, в которой запутанные места обернуты в тривиальный класс , который затем дает вам единственную ссылку, которую можно атомарно заменить без блокировки. Кодирование без блокировок - простое занятие в .NET, так как оно менее хлопотно для объектов повторных попыток управления памятью в тех редких случаях, когда оптимистический путь терпит неудачу.
Достаточно сказать, что, по моему опыту, нет никакого существенного аспекта параллелизма без блокировок, которого я не смог бы достичь в C # /. NET / CLR , даже если иногда это немного шероховатый по краям, как вы можете убедиться из https://stackoverflow.com/a/5589515/147511.