Отказ от ответственности: Мои сообщения являются, по-видимому, всегда подробными. Если Вы, оказывается, знаете ответ на вопрос о заголовке, не стесняйтесь просто отвечать на это, не читая мое расширенное обсуждение ниже.
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>
по-видимому, имеет a where T : class
ограничение, и я не понимаю почему. Я имею в виду, возможно, это - потому что уже существуют перегрузки для CompareExchange
это принимает Int32
, Int64
, Double
, и т.д.; но это едва кажется хорошим объяснением. В моем случае, например, было бы довольно удобно смочь использовать Aggregate<T>
метод для выполнения широкого спектра атомарных вычислений.
Interlocked.CompareExchange
предназначен для реализации с помощью собственных атомарных инструкций, предоставляемых непосредственно процессором. Бессмысленно, чтобы нечто подобное использовало блокировку
внутри (оно предназначено для сценариев без блокировок).
Процессоры, предоставляющие атомарную инструкцию сравнения, естественно, поддерживают ее в виде небольших, "регистрового размера" операций (например, самая большая инструкция сравнения-обмена в процессоре Intel x64 - cmpxchg16b
, которая работает со 128-битными значениями).
Произвольный тип значения может быть потенциально больше этого, и сравнение-обмен с ним может быть невозможен с помощью одной инструкции. Сравнить-обменять ссылочный тип очень просто. Независимо от его общего размера в памяти, вы будете сравнивать и копировать маленький указатель известного размера. Это также верно для примитивных типов, таких как Int32
и Double
- все они маленькие.
Я подозреваю, что Interlocked.CompareExchange
просто выполняет атомарную замену указателя под капотом.
Попытка сделать это с типом значения, скорее всего, не даст ожидаемых результатов.
Можно, конечно, упаковать типы значений в объект
перед их использованием.
Потому что эта перегрузка специально предназначена для сравнения и обмена ссылками. Он не выполняет проверку равенства с помощью метода Equals (). Поскольку у типа значения никогда не будет ссылочного равенства со значением, с которым вы его сравниваете, я предполагаю, что они ограничили T классом, чтобы предотвратить неправильное использование.