Disclaimer: i miei post sono apparentemente sempre prolissi. Se ti capita di conoscere la risposta alla domanda del titolo, sentiti libero di rispondere semplicemente senza leggere la mia discussione estesa qui sotto.Perché interlocked.CompareExchange <T> supporta solo i tipi di riferimento?
La classe System.Threading.Interlocked
fornisce alcuni metodi molto utili per aiutare nella scrittura di codice thread-safe. Uno dei metodi più complessi è CompareExchange
, che può essere utilizzato per calcolare un totale parziale che può essere aggiornato da più thread.
Poiché l'uso di CompareExchange
è un po 'complicato, ho pensato che un'idea piuttosto di buon senso per fornire alcuni metodi di supporto per esso:
// 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; });
}
Ora, forse io sono solo colpevole di essere generic-felice (Lo ammetto, questo è spesso vero); ma mi sembra stupido per me limitare la funzionalità fornita dai metodi sopra solo ai valori double
(o, più precisamente, per me dover scrivere versioni sovraccaricate dei metodi precedenti per ogni tipo che voglio supportare). Perché non posso farlo?
// 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)
)
);
}
non posso farlo perché Interlocked.CompareExchange<T>
ha apparentemente un vincolo where T : class
e non capisco perché. Voglio dire, forse perché ci sono già sovraccarichi per CompareExchange
che accettano Int32
, Int64
, Double
, ecc .; ma a stento sembra una buona logica. Nel mio caso, ad esempio, sarebbe molto utile poter usare il metodo Aggregate<T>
per eseguire una vasta gamma di calcoli atomici.
Penso che questo sia stato messo a fuoco per me una volta che ho letto questo nella documentazione MSDN poco dopo aver postato la domanda (per il sovraccarico che accetta i parametri di 'Object'):" Gli oggetti vengono confrontati per l'uguaglianza di riferimento, piuttosto che "Object.Equals". Di conseguenza, due istanze in scatola dello stesso tipo di valore (ad esempio, il numero intero 3) sembrano sempre non uguali e non viene eseguita alcuna operazione. Non utilizzare questo sovraccarico con tipi di valore. " Ovviamente - non userà il metodo 'Equals', ma piuttosto controllerà i riferimenti stessi. –
Quindi, chiaramente, la mia unica scelta sarebbe in effetti scrivere tutti quei metodi sovraccaricati (per 'int',' long', ecc.) Dopotutto - se volessi davvero seguire questa strada. Destra? –
@Dan: Non sono sicuro di come lo farebbe per un tipo di valore più grande * atomicamente * senza usare un 'lock' (scambiando un gruppo di valori interi). Il punto di confronto-scambio è che è atomico; cioè, non ci sarà un punto nel tempo in cui è possibile osservare una parte dell'operazione è fatta e l'altra parte non lo è. Mettere una serie di scambi di confronti l'uno sull'altro non aiuta a rendere l'operazione globale atomica (ogni parte è atomica, ma l'operazione nel suo insieme, non lo è). –