Почему же блокируется.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 7

3 ответа:

Interlocked.CompareExchange предназначен для реализации с помощью собственных атомарных инструкций, предоставляемых непосредственно процессором. Бессмысленно, чтобы что-то подобное использовалось внутри lock (оно предназначено для сценариев без блокировки).

Процессоры, обеспечивающие атомарную инструкцию compare exchange, естественно, поддерживают ее в виде небольших операций "регистрового размера" (например, самая большая инструкция compare-exchange на процессоре Intel x64-это cmpxchg16b, работающая со 128-битными значениями).

Произвольный тип значения может будьте потенциально больше этого и сравните-обмен может быть невозможен с помощью одной инструкции. Сравнить-обмен ссылочным типом очень прост. Независимо от его общего размера в памяти, вы будете сравнивать и копировать небольшой указатель известного размера. Это также верно для примитивных типов, таких как Int32 и Double-все они малы.

Потому что эта перегрузка специально предназначена для сравнения и обмена ссылками. Он не выполняет проверку на равенство с помощью метода Equals (). Поскольку тип значения никогда не будет иметь ссылочного равенства со значением, с которым вы его сравниваете, я предполагаю, что они ограничили T классом, чтобы предотвратить неправильное использование.

Я бы предположил, что Interlocked.CompareExchange<T> просто выполняет атомарный обмен указателями под капотом.

Попытка сделать это с типом значения, скорее всего, не даст ожидаемых результатов.

Можно, конечно, вставлять типы значений в object перед их использованием.