Я чувствую, что на этой странице присутствует некоторая расплывчатая путаница. Во-первых, комментатор прав в том, что вопрос содержит опасное предположение:
int i = 5;
Interlocked.CompareExchange(ref i, 10, 5);
После этой команды int i будет иметь значение = 10.
Нет, только если значение i
за это время не изменилось на значение, отличное от 5
. Хотя в показанном здесь коде это кажется маловероятным, весь смысл использования CompareExchange
в том, что это должно быть возможно, поэтому здесь это критическая техническая сторона. Я беспокоюсь, что OP может не понять цель Interlocked.CompareExchange
, особенно потому, что он не проверяет возвращаемое значение (см. Ниже).
Теперь текст исходного вопроса был:
«Есть ли способ сделать это? Я хочу сравнить два экземпляра класса и присвоить одному из них значение на основе сравнения».
Поскольку у слова «это» нет жизнеспособного антецедента, нам, вероятно, следует рассмотреть в качестве вопроса предложение, которое следует после, с перефразированием:
«Есть ли способ сравнить два экземпляра класса и присвоить одному из них значение на основе сравнения?»
К сожалению, этот вопрос до сих пор неясен или, возможно, имеет мало общего с атомарными операциями. Во-первых, вы не можете «присвоить [экземпляру класса] значение». Это просто не имеет смысла. Ссылка на экземпляр класса является значением, но нет никакого способа «назначить» что-либо самому экземпляру класса. Это существенное отличие от типов значений, которые можно назначать друг другу. Вы можете создать экземпляр, используя оператор new
, но вы все равно получите ссылку на него. Опять же, это может показаться формальностью, но это критические моменты, если вопрос действительно касается параллелизма без блокировок.
Затем функция Interlocked.CompareExchange
не обуславливает место хранения значением, а, скорее, условно сохраняет значение в (заданном) месте, что означает, что она либо сохраняет значение (успех) или оставляет место хранения неизменным (сбой), при этом надежно указывая, какое из них произошло.
Это означает, что фраза «на основе сравнения» не является исчерпывающей в отношении того, какими именно должны быть альтернативные действия. Глядя на более раннюю часть вопроса OP, можно предположить, что вопрос пытается условно манипулировать ссылками на экземпляры, а атомарность - отвлекающий маневр. Трудно знать, потому что, как отмечалось выше, 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
операций; на самом деле все наоборот: если вам действительно нужно выполнить атомарную операцию, которая выбирает между двумя разными местами хранения или иным образом влияет на несколько мест памяти, просто использовать дизайн, в котором запутанные места обертываются тривиальным, содержащим class, который затем дает вам единственную ссылку, которую можно атомарно поменять местами без блокировки. Безблокировочное кодирование в .NET - легкий ветерок, поскольку в редких случаях, когда оптимистичный путь не работает, с ним легче справляются объекты повторных попыток с управлением памятью.
Достаточно сказать, что, по моему опыту, нет существенного аспекта параллелизма без блокировок, которого я не смог бы достичь в C # /. NET / CLR, даже если это иногда немного грубо. по краям, как вы могли убедиться из https://stackoverflow.com/a/5589515/147511.
person
Glenn Slayden
schedule
01.02.2017
y
имеет одинаковое значение. - person Bhargav Mangipudi   schedule 14.07.2011CompareExchange
, равно 5 (или 10 :-)). Возможно, это был упрощенный пример кода, но я видел много неработающего кода, когда люди забывают, что вам нужно проверить, чтоCompareExchange
был успешным, прежде чем продолжить. - person Damien_The_Unbeliever   schedule 14.07.2011