Почему же блокируется.CompareExchange поддерживает только ссылочные типы?
отказ от ответственности: мои сообщения, по-видимому, всегда многословны. Если вы случайно знаете ответ на вопрос о названии, не стесняйтесь просто ответить на него, не читая мое расширенное обсуждение ниже.
Класс System.Threading.Interlocked
предоставляет некоторые очень полезные методы для помощи в написании потокобезопасного кода. Одним из наиболее сложных методов является CompareExchange
, который может быть использован для вычисления текущего итога, который может быть обновлен из нескольких потоков.
Так как использование CompareExchange
немного сложно, Я подумал, что это довольно здравая идея, чтобы предоставить некоторые вспомогательные методы для этого:
// code mangled so as not to require horizontal scrolling
// (on my monitor, anyway)
public static double Aggregate
(ref double value, Func<double, double> aggregator) {
double initial, aggregated;
do {
initial = value;
aggregated = aggregator(initial);
} while (
initial != Interlocked.CompareExchange(ref value, aggregated, initial)
);
return aggregated;
}
public static double Increase(ref double value, double amount) {
return Aggregate(ref value, delegate(double d) { return d + amount; });
}
public static double Decrease(ref double value, double amount) {
return Aggregate(ref value, delegate(double d) { return d - amount; });
}
Теперь, возможно, я просто виноват в том, что я универсально счастлив (я признаю, что это часто верно); но мне кажется глупым ограничивать функциональность, предоставляемую вышеупомянутыми методами, только значениямиdouble
(или, точнее, мне нужно писать перегруженные версии вышеупомянутых методов для каждого типа, который я хочу поддерживать). Почему я не могу этого сделать?
// the code mangling continues...
public static T Aggregate<T>
(ref T value, Func<T, T> aggregator) where T : IEquatable<T> {
T initial, aggregated;
do {
initial = value;
aggregated = aggregator(initial);
} while (
!initial.Equals(
Interlocked.CompareExchange<T>(ref value, aggregated, initial)
)
);
}
Я не могу этого сделать, потому что Interlocked.CompareExchange<T>
, по-видимому, имеет ограничение where T : class
, и я не понимаю, почему. Я имею в виду, может быть, это потому, что уже есть перегрузки для CompareExchange
, которые принимают Int32
, Int64
, Double
, и т.д.; но это вряд ли кажется хорошим обоснованием. В моем случае, например, было бы очень удобно использовать метод Aggregate<T>
для выполнения широкого спектра атомарных вычислений.
3 ответа:
Процессоры, обеспечивающие атомарную инструкцию compare exchange, естественно, поддерживают ее в виде небольших операций "регистрового размера" (например, самая большая инструкция compare-exchange на процессоре Intel x64-это
Interlocked.CompareExchange
предназначен для реализации с помощью собственных атомарных инструкций, предоставляемых непосредственно процессором. Бессмысленно, чтобы что-то подобное использовалось внутриlock
(оно предназначено для сценариев без блокировки).cmpxchg16b
, работающая со 128-битными значениями).Произвольный тип значения может будьте потенциально больше этого и сравните-обмен может быть невозможен с помощью одной инструкции. Сравнить-обмен ссылочным типом очень прост. Независимо от его общего размера в памяти, вы будете сравнивать и копировать небольшой указатель известного размера. Это также верно для примитивных типов, таких как
Int32
иDouble
-все они малы.
Потому что эта перегрузка специально предназначена для сравнения и обмена ссылками. Он не выполняет проверку на равенство с помощью метода Equals (). Поскольку тип значения никогда не будет иметь ссылочного равенства со значением, с которым вы его сравниваете, я предполагаю, что они ограничили T классом, чтобы предотвратить неправильное использование.