C# Monitor.TryEnter(锁定对象、超时)重载是否不安全?(.net)
在代码审查期间,有人建议我这样做C# Monitor.TryEnter(锁定对象、超时)重载是否不安全?(.net),c#,C#,在代码审查期间,有人建议我这样做 bool acquiredLock = false; try { Monitor.TryEnter(lockObject, 500, ref acquiredLock); if (acquiredLock) { // do something } else { // fallback strategy } } finally { if (acquiredLock) {
bool acquiredLock = false;
try {
Monitor.TryEnter(lockObject, 500, ref acquiredLock);
if (acquiredLock) {
// do something
}
else {
// fallback strategy
}
}
finally
{
if (acquiredLock)
{
Monitor.Exit(lockObject);
}
}
而不是简单的
if (Monitor.TryEnter(lockObject, 500)) {
try {
// do something...
}
finally {
Monitor.Exit(lockObject);
}
} else {
// fallback strategy
}
这有什么区别?如果第二个代码会出现错误,那么第一个代码怎么可能不会出现错误呢?当第二个代码没有出现时,您的第一个代码片段正在退出监视器。你想在完成关键块后释放监视器。除了其他海报提出的关于退出的完全有效的观点之外。。。声明它可以抛出三个异常中的一个,因此从技术上讲,它可以在特定情况下轰炸您的应用程序。即使该方法名为
TryEnter
,它实际上抛出异常,您需要正确处理它们。如果它抛出一个异常,而您不处理它并释放监视器,则可能出现死锁情况
这可能有点过于谨慎,因为若您查看它实际抛出的异常,它很可能会在尝试获取锁之前抛出它们。但这是一个实现细节,您无法确定
您可能可以在上检查此方法是如何实现的,但正如我所说,这是一个实现细节。假设您在第二个代码片段中实际调用了
Monitor.Exit
,差异将在中解释:
此重载始终设置传递给ref
参数locktaked
的变量值,即使该方法引发异常,因此变量值是测试是否必须释放锁的可靠方法
换句话说,对于第二个代码段,在获取锁之后但在方法返回之前抛出异步异常(例如,正在中止的线程)是可行的。即使使用finally
块,也无法轻松判断是否需要释放锁。使用ref
参数,“获取监视器”和“ref
参数设置为true
”操作是原子的-当方法退出时,变量不可能有错误的值,但是它退出了
从C#4开始,当针对支持此重载的平台时,这也是C#编译器生成的代码。因为您从未退出锁?@SLaks抱歉,这是由于问题中的错误。这不是真正的问题。我会解决这个问题。如果代码仍然正常工作,您必须在混合中抛出一个Thread.Abort(),并进行推理。背景,x64抖动需要搞乱它。可能是一个重复的是,这看起来像一个重复对我来说,即使所有的功能签名是不同的!对不起,那是因为问题中的一个错误。这不是真正的问题。我来解决这个问题。那些记录在案的异常都是参数验证异常。在获得锁后,您似乎不太可能获得任何一个。但是:语句Monitor.TryEnter没有包含在捕获这些验证异常的内容中,至少没有包含在显示的代码段中。它可能会在运行时爆炸。我的意思是,如果你得到任何一个异常,显然你没有锁。您甚至不需要ref locktaked参数来知道这一点。