我在C#中寻找等待锁定语句的等价物。有些人建议以下列方式使用二进制SemaphoreSlim
:
await semaphore.WaitAsync().ConfigureAwait(false);
try
{
//inner instructions
}
finally
{
semaphore.Release();
}
我知道它有一些问题(例如它不可重入),但我最关心的是指令重新排序。
在普通的旧锁定语句中,我们保证不会将来自锁的内部指令移出锁定语句之外(之前或之后)。这个信号量解决方案是否同样适用?据我所见,documentation没有提到这个问题。
答案 0 :(得分:1)
SemaphoreSlim
以及几乎所有其他同步构造都是在内部使用Monitor
(或构建在Monitor
之上的其他类型)构建的,这正是如何实施lock
,为您提供相同的保证。
答案 1 :(得分:0)
SemaphoreSlim
保证是隐含的。它被描述为Overview of Synchronization Primitives中的锁定同步原语。
答案 2 :(得分:0)
我不是记忆模型的专家,但现在我认为我们有这些保证。
作为Servy has pointed out,Wait
和Release
方法都使用了Monitor
。但是,Monitor
就是Wait
。
在Monitor.Exit
方法结束时,就在if (lockTaken)
{
m_waitCount--; //m_waitCount is volatile
Monitor.Exit(m_lockObj);
}
调用之前,一个volatile字段会递减。
Release
据我所知,在volatile字段上使用的递减运算符将引入'acquire'和'release'操作,阻止以下指令在它之前重新排序。
对于lock (m_lockObj)
{
//m_currentCount is volatile
if (m_maxCount - m_currentCount < releaseCount)
{
throw new SemaphoreFullException();
}
m_currentCount += releaseCount;
方法,情况类似。开始时我们同时拥有锁定获取和易失性读写操作。
SemaphoreSlim
特别感谢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()
中易变字段的重要性。
编辑:演示自身锁定(没有额外的易失操作)可能不够的情况的示例。
(2)
当尚未获取信号量时,来自关键部分(1)
的读取指令可以重新排序到GC_CALLTYPE
(另一个线程可能仍在关键部分中工作)。