Disclaimer: My posts are apparently always verbose. If you happen to know the answer to the title question, feel free to just answer it without reading my extended discussion below.
The System.Threading.Interlocked
class provides some very useful methods to assist in writing thread-safe code. One of the more complex methods is CompareExchange
, which can be used for computing a running total that may be updated from multiple threads.
Since the use of CompareExchange
is a bit tricky, I thought it a rather common-sense idea to provide some helper methods for it:
// 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; });
}
Now, perhaps I am just guilty of being generic-happy (I will admit, this is often true); but it does feel silly to me to restrict the functionality provided by the above methods to double
values only (or, more accurately, for me to have to write overloaded versions of the above methods for every type I want to support). Why can't I do this?
// 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)
)
);
}
I can't do this because Interlocked.CompareExchange<T>
apparently has a where T : class
constraint, and I don't understand why. I mean, maybe it's because there are already overloads for CompareExchange
that accept Int32
, Int64
, Double
, etc.; but that hardly seems a good rationale. In my case, for example, it would be quite handy to be able to use the Aggregate<T>
method to perform a wide range of atomic calculations.