Reading on the Interlocked Anything Pattern as coined by Jeffrey Richter in CLR via C# 4th Edition he gives the following example demonstrating that we can do more than just Add
for Interlocked
public static Int32 Maximum(ref Int32 target, Int32 value) {
Int32 currentVal = target, startVal, desiredVal;
// Don't access target in the loop except in an attempt
// to change it because another thread may be touching it
do {
// Record this iteration's starting value
startVal = currentVal;
// Calculate the desired value in terms of startVal and value
desiredVal = Math.Max(startVal, value);
// NOTE: the thread could be preempted here!
// if (target == startVal) target = desiredVal
// Value prior to potential change is returned
currentVal = Interlocked.CompareExchange(ref target, desiredVal, startVal);
// If the starting value changed during this iteration, repeat
} while (startVal != currentVal);
// Return the maximum value when this thread tried to set it
return desiredVal;
}
A very similar version of pattern is to be found for the add
and remove
methods generated by the C# compiler for events:
[CompilerGenerated]
{
EventHandler eventHandler = this.MyEvent;
EventHandler eventHandler2;
do
{
eventHandler2 = eventHandler;
EventHandler value2 = (EventHandler)Delegate.Combine(eventHandler2, value);
eventHandler = Interlocked.CompareExchange(ref this.MyEvent, value2, eventHandler2);
}
while ((object)eventHandler != eventHandler2);
}
However the one big difference is how we get the “target” value – ref argument vs. field access.
The original example using the ref argument, what is there to prevent the target value from changing before it is assigned to the temp value via:
Int32 currentVal = target, startVal, desiredVal;
For example by another thread changing the value just as we jump to the beginning of the method?