A followup question can be found here, based on this question and it's answer.
Considering:
Interlocked.CompareExchange
generates a memory barrier,- Intended to be used with very fast operations (like generating an
id
), SpinWait
starts sleeping in milliseconds after 10 or so spins (and does some other smart stuff),
What are characteristics (pros & cons) of this code?
public class SpinMutex
{
const long Locked = 1;
const long Unlocked = 0;
long _locked;
public SpinMutex() { _locked = Unlocked; }
public void Lock()
{
SpinWait.SpinUntil(() => Interlocked.CompareExchange(ref _locked, Locked, Unlocked) == Locked);
}
public bool Lock(int millisecondsTimeout)
{
return SpinWait.SpinUntil(() => Interlocked.CompareExchange(ref _locked, Locked, Unlocked) == Locked, millisecondsTimeout);
}
public bool Lock(TimeSpan timeout)
{
return SpinWait.SpinUntil(() => Interlocked.CompareExchange(ref _locked, Locked, Unlocked) == Locked, timeout);
}
public void Unlock()
{
Interlocked.Exchange(ref _locked, Unlocked);
}
}
And some extra points to consider:
- Does it help if
SpinMutex
be defined asstruct
instead of aclass
(I'm aware there would be hurdles with sayreadonly
fields ofstruct
type and the like)? - Should
Interlocked.CompareExchange
be used inUnlock
too, instead ofInterlocked.Exchange
to ensure generating of a memory barrier? OrInterlocked.Exchange
generates a memory barrier itself? Is a memory barrier needed at all atUnlock
?