Do semaphores prevent instruction reordering?

烈酒焚心 提交于 2019-12-01 04:09:39

问题


I was looking for an awaitable equivalent of lock statements in C#. Some people suggest using a binary SemaphoreSlim in the following way:

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

I know it has some issues (e.g. it's not reentrant), but my biggest concern is with the instruction reeordering.

In plain old lock statements we have a guarantee that no inner instruction from the lock will be moved outside (before or after) the lock statement. Does the same hold for this semaphore solution? As far as I can see, the documentation doesn't mention this problem.


回答1:


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.




回答2:


The SemaphoreSlim guarantee is kind of implicit. It's described as a locking synchronization primitive in Overview of Synchronization Primitives.




回答3:


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).



来源:https://stackoverflow.com/questions/40979715/do-semaphores-prevent-instruction-reordering

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!