Do semaphores prevent reordering of commands?

I was looking for the expected equivalent of lock statements in C #. Some people suggest using SemaphoreSlim binary as follows:

 await semaphore.WaitAsync().ConfigureAwait(false); try { //inner instructions } finally { semaphore.Release(); } 

I know that he has some problems (for example, he is not reentrant), but my biggest problem is with the reorientation of teams.

In simple legacy statements, we guarantee that the internal lock instruction will not be moved outside (before or after) the lock statement. Does the same apply to this semaphore decision? As far as I understand, the documentation does not mention this problem.

+4
source share
3 answers

SemaphoreSlim and almost all other synchronization constructs are built using Monitor (or other types that are built on top of Monitor ) inside, which exactly matches the lock implemented, giving you the same guarantees.

0
source

SemaphoreSlim warranty is implicit. It is described as a synchronization primitive in Synchronization Primitives Overview .

0
source

I am not an expert in memory models, but now I think we have these guarantees.

As stated by Servy , the Wait and Release methods use Monitor under the hood. However, << 22> may not be enough .

At the end of the Wait method, just before the call to Monitor.Exit , the volatile field is reduced.

 if (lockTaken) { m_waitCount--; //m_waitCount is volatile Monitor.Exit(m_lockObj); } 

As I understand it, the decrement operator used in the volatile field introduces the acquire and release operations, blocking the following instructions from reordering to it.

As for the Release method, the situation is similar. In the beginning, we have both a lock collection and an unstable read / write operation.

 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 mutable fields in SemaphoreSlim .

EDIT . An example that demonstrates a situation where interlocks alone (without additional volatile operations) may be insufficient.

 // 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() 

The read instruction from critical section (2) can be reordered to (1) when the semaphore has not yet been received (another thread can still work in the critical section).

0
source

Source: https://habr.com/ru/post/1012932/


All Articles