SemaphoreSlim
:
lock
进行同步锁定,使用 busy
布尔值进行异步锁定。现在我什么都用 SemaphoreSlim
。
private SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 1);
private void DoStuff()
{
semaphoreSlim.Wait();
try
{
DoBlockingStuff();
}
finally
{
semaphoreSlim.Release();
}
}
对
private object locker = new object();
private void DoStuff()
{
lock(locker)
{
DoBlockingStuff();
}
}
是否有任何同步情况下我应该更喜欢使用
lock
而不是 SemaphoreSlim
?如果有,它们是什么?
lock
相对于SemaphoreSlim
的优点:
lock
是可重入的,而SemaphoreSlim
则不是。因此,使用 lock
进行编程更加宽容。如果您的应用程序中存在罕见的路径,您两次获取相同的锁,则 lock
将成功获取它,而 SemaphoreSlim
将死锁。
lock
是围绕Monitor
类的语法糖。换句话说,C# 中有对 Monitor
的语言支持,但没有对 SemaphoreSlim
的语言支持。所以使用lock
相对来说更方便,也更简洁。
您可以使用
lock
编写更健壮的代码,因为您可以在辅助方法中添加调试断言,表明锁定已获取:Debug.Assert(Monitor.IsEntered(_locker));
Monitor.LockContentionCount
属性获取争用统计信息: “获取尝试获取监视器锁定时发生争用的次数。” SemaphoreSlim
类没有可用的统计信息。
SemaphoreSlim
是IDisposable
,所以你必须考虑何时(以及是否)处置它。你能不扔掉就离开吗?您是否过早地丢弃它并冒着ObjectDisposedException
的风险?这些问题您不必用 lock
来回答。
lock
可以在线程中止的情况下生存。它是由 C# 编译器翻译的 like this:
bool lockTaken = false;
try
{
Monitor.Enter(obj, ref lockTaken);
DoBlockingStuff();
}
finally
{
if (lockTaken)
{
Monitor.Exit(obj);
}
}
Monitor.Enter
已仔细编码,以便万一线程中止,lockTaken
将具有正确的值。相反,SemaphoreSlim.Wait
通常在try
/finally
块之外调用,因此存在一个小窗口,可以在不释放锁的情况下中止当前线程,从而导致死锁。
Thread.Abort
方法的支持,因此您可以正确地说最后一点仅具有理论价值。