假设我有以下代码:
public class SomeClass()
{
private readonly object _lock = new object();
public void SomeMethodA()
{
lock (_lock)
{
SomeHelperMethod();
//do something that requires lock on _lock
}
}
public void SomeMethodB()
{
lock (_lock)
{
SomeHelperMethod();
//do something that requires lock on _lock
}
}
private void SomeHelperMethod()
{
lock (_lock)
{
//do something that requires lock on _lock
}
}
}
锁定SomeHelperMethod
内部似乎是多余和浪费的,因为所有来电者都已锁定。但是,从SomeHelperMethod
中删除锁定似乎很危险,因为我们稍后可能会重构代码并忘记在调用_lock
之前锁定SomeHelperMethod
对象。
理想情况下,我可以通过断言当前线程在_lock
内的SomeHelperMethod
上拥有锁来解决这个问题:
private void SomeHelperMethod()
{
Debug.Assert(Monitor.HasLock(_lock));
//do something that requires lock on _lock
}
但似乎没有这样的方法。 Monitor.TryEnter
不帮助,因为锁是可重入的。因此,如果当前线程已拥有锁,TryEnter
仍将成功并返回true
。它失败的唯一时间是另一个线程拥有锁并且呼叫超时。
那么,这种方法存在吗?如果没有,为什么?它对我来说似乎没什么危险,因为它只是告诉你当前线程(不是另一个线程)是否拥有锁。
答案 0 :(得分:12)
.NET 4.5使用Monitor.IsEntered(object)支持此功能。
答案 1 :(得分:5)
锁定或重新锁定实际上是免费的。这种低成本的缺点是您没有其他同步机制那么多的功能。只要锁是至关重要的,我就会锁定,如上所述。
如果您绝对想要省略“不必要的”锁定而没有潜在重构所涉及的风险,请添加注释。如果有人更改了您的代码并且它已经中断,那么就会有一条注释可以解释原因。如果他们仍然无法弄明白,那就是他们的问题,而不是你的问题。另一种方法是创建一个类用作锁定对象,该类包含一个可以轻弹的布尔值。但是,通过这样做引入的开销(包括不是免费的try / finally块)可能不值得。
答案 2 :(得分:3)
您的请求存在根本问题。假设有一个IsLockHeld()方法,你可以像这样使用它:
if (not IsLockHeld(someLockObj)) then DoSomething()
这在设计上无法奏效。你的线程可以在if()语句和方法调用之间被抢占。允许另一个线程运行并锁定该对象。现在DoSomething()将运行,即使锁被保持。
避免这种情况的唯一方法是尝试锁定并查看它是否有效。 Monitor.TryEnter()。
您在Windows中看到完全相同的模式。除了尝试打开文件之外,无法检查文件是否被其他进程锁定。出于同样的原因。
答案 3 :(得分:2)
但似乎没有这样的方法。 Monitor.TryEnter没有帮助,因为锁是可重入的。因此,如果当前线程已拥有锁,TryEnter仍将成功并返回true。它失败的唯一时间是另一个线程拥有锁并且呼叫超时。
我发现了一种类似于Monitor.TryEnter()的解决方法,但没有可重入的问题。基本上不使用Monitor.TryEnter(),而是使用Monitor.Pulse()。如果当前线程没有锁定,则此函数抛出SynchronizationLockException异常。
示例:
try
{
System.Threading.Monitor.Pulse(lockObj);
}
catch (System.Threading.SynchronizationLockException /*ex*/)
{
// This lock is unlocked! run and hide ;)
}
文档:http://msdn.microsoft.com/en-us/library/system.threading.monitor.pulse.aspx
答案 4 :(得分:1)
行, 我现在明白你的问题了。 从概念上讲,我现在正在考虑一个信号量对象。您可以轻松检查信号量值(大于或等于0的值表示资源可用)。此外,这实际上并不通过递增或递减信号量值来锁定正在完成的资源。 Semaphore对象存在于.NET框架中,但我没有使用它,因此我无法提供正确的示例。如果有必要,我可以构建一个简单的示例并在此处发布。让我知道。
亚历。
L.E。 如果你真的可以控制应用程序中的同步机制,我现在不确定。如果您只能访问可能被锁定的对象,我的解决方案可能(再一次)证明没有用。
答案 5 :(得分:1)
请参阅我对类似问题的回答:Test a lock with out acquiring it?
答案 6 :(得分:0)
我希望这是可能的,这将使我的代码更加清晰。
答案 7 :(得分:0)
我对.NET编程没有任何经验,但在我自己的代码(Delphi)中,我曾经通过在一个关键的部分类中使用自定义的Acquire()和Release()方法来实现这样的断言,其中包含一个私有成员变量获取cs的线程的id。在EnterCriticalSection()之后,写入GetCurrentThreadID的返回值,并在LeaveCriticalSection()之前重置字段值。
但我不知道.NET是否允许类似的事情,或者你是否可以在你的程序中实现它......
答案 8 :(得分:0)
我有类似的要求检查锁是否被当前线程锁定,所以我可以这样做。
public void Method1()
{
if(!lockHeld()) lock();
//DO something
}
public void Method2()
{
if(!lockHeld()) lock();
//DO something
}
public void Method3()
{
if(!lockHeld()) lock();
//Do something
unlock()
}
所以我为自己写了一个课:
public class LockObject
{
private Object internalLock;
private bool isLocked;
public bool IsLocked
{
get { lock (internalLock) return isLocked; }
private set { lock (internalLock) isLocked = value; }
}
public LockObject()
{
internalLock = new object();
}
public void UsingLock(Action method)
{
try
{
Monitor.Enter(this);
this.IsLocked = true;
method();
}
finally
{
this.IsLocked = false;
Monitor.Exit(this);
}
}
}
然后我可以用它作为:
public void Example()
{
var obj = new LockObject();
obj.UsingLock(() =>
{
//Locked, and obj.IsLocked = true
});
}
注意:我在这个类上省略了Lock()Unlock()方法,但几乎只是Monitor.Enter / Exit的包装器,它将IsLocked设置为true。这个例子只是说明了它在样式方面与lock(){}非常相似。
可能没必要,但这对我有用。
答案 9 :(得分:0)
如果您在.NET 4.5下需要,请参阅@Dave中接受的答案。
但是,如果您需要在.NET 3.5或4下使用此功能,则可以按如下方式替换ReaderWriterLockSlim的Monitor锁的使用。
According to Joeseph Albahari这将大约加倍(无争议)锁定开销,但它仍然非常快(估计为40nS)。 (免责声明,该页面未说明测试是否使用递归锁定策略完成。)
public class SomeClass()
{
private readonly ReaderWriterLockSlim _lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
public void SomeMethodA()
{
_lock.EnterWriteLock();
try
{
SomeHelperMethod();
//do something that requires lock on _lock
}
finally
{
_lock.ExitWriteLock();
}
}
private void SomeHelperMethod()
{
Debug.Assert(_lock.IsWriteLockHeld);
//do something that requires lock on _lock
}
}
如果您认为这种性能太高,您当然可以围绕Monitor锁编写自己的包装类。但是,其他帖子提到了一个误导的布尔标志,因为问题不是“任何线程都持有锁?” (这是一个愚蠢而危险的问题)。正确的问题是“这个线程是否锁定了?”。没问题,只需将'flag'设为ThreadLocal计数器(用于递归锁支持)并确保在Monitor.Enter之后和Monitor.Exit之前完成更新。作为改进断言,计数器在“解锁”时不会降至0以下,因为这表示不平衡的进入/退出呼叫。
包装器的另一个原因是避免让某人在_lock.EnterReadLock调用中滑倒。根据课程的用法,这些可能实际上有帮助,但是令人惊讶的编码人员并不理解读者 - 作家锁。
答案 10 :(得分:-1)
不是最好的事情,但......
bool OwnsLock(object someLockObj)
{
if (Monitor.TryEnter(someLockObj))
{
Monitor.Exit();
return false;
}
return true;
}
Debug.Assert(OwnsLock(_someLockObject), "_someLockObject should be locked when this method is called")