I was looking for an awaitable equivalent of lock statements in C#. Some people suggest using a binary SemaphoreSlim
in the following way:
await
I am no expert in memory models, but now I think that we have those guarantees.
As Servy has pointed out, both the Wait
and Release
methods use a Monitor
under the hood. However, a Monitor
on its own may not be enough.
At the end of the Wait
method, right before the Monitor.Exit
call, a volatile field is decremented.
if (lockTaken)
{
m_waitCount--; //m_waitCount is volatile
Monitor.Exit(m_lockObj);
}
As far as I understand, the decrement operator used on a volatile field will introduce both the 'acquire' and 'release' operations, blocking the following instructions from being reordered before it.
As for the Release
method, the situation is analogous. At the beginning we have both the lock acquisition and volatile read-write operation as well.
lock (m_lockObj)
{
//m_currentCount is volatile
if (m_maxCount - m_currentCount < releaseCount)
{
throw new SemaphoreFullException();
}
m_currentCount += releaseCount;
Special thanks to Joe Duffy for pointing out the importance of the volatile fields in the SemaphoreSlim
.
EDIT: An example demonstrating a situation where the locks on their own (without additional volatile operations) may not be enough.
// Semaphore.Wait()
lock (syncRoot)
{
// (1)
// acquire semaphore
}
// end of Semaphore.Wait()
// the critical section guarded by the 'semaphore lock' (2)
// Semaphore.Release()
lock (syncRoot)
{
// release semaphore
}
// end of Semaphore.Release()
A read instruction from the critical section (2)
could be reordered to (1)
, when the semaphore is not yet acquired (another thread might still be working in a critical section).
SemaphoreSlim
, and pretty much all of the other synchronization constructs, are built using a Monitor
(or other types that are built on top of a Monitor
) internally, which is exactly how a lock
is implemented, giving you the same guarantees.
The SemaphoreSlim
guarantee is kind of implicit. It's described as a locking synchronization primitive in Overview of Synchronization Primitives.