这是我正在修复的一个大型多线程项目(我没写过)。应用程序挂起了我正在追踪的一些锁。
我通过Monitor.TryEnter
替换了所有“锁定”语句,因此我可以设置一个等待期。我偶尔会使用Monitor.Exit
获得例外。
原始风格是
private List<myClass> _myVar= new List<myClass>();
if (_myVar != null)
{
lock (_myVar)
{
_myVar = newMyVar; // Where newMyVar is another List<myClass>
}
}
我用以下方法替换了我上面的所有锁:
if (_myVar != null)
{
bool lockTaken = false;
try
{
Monitor.TryEnter(_myVar, new TimeSpan(0, 0, 5), ref lockTaken);
if (lockTaken)
{
_myVar = newMyVar; // Where newMyVar is another List<myClass>
}
}
finally
{
if (lockTaken) Monitor.Exit(_myVar);
}
}
我得到的例外是
SynchronizationLockException调用了对象同步方法 来自不同步的代码块
。如果这是真的,为什么原始的lock语句也没有抛出异常?
将Monitor.Exit
置于try catch中是否安全,如果有异常则忽略它?
答案 0 :(得分:4)
应该非常清楚为什么要在新代码中获取异常。 如果锁定,则解锁的对象不是被锁定的对象。锁定采用对象,而不是变量。 深度错误原始代码的正确翻译是
// THIS CODE IS COMPLETELY WRONG; DO NOT USE IT
if (_myVar != null)
{
bool lockTaken = false;
var locker = _myVar;
try
{
Monitor.TryEnter(locker, new TimeSpan(0, 0, 5), ref lockTaken);
if (lockTaken)
{
_myVar = newMyVar; // where newMyVar is another List<myClass>
}
}
finally
{
if (lockTaken) Monitor.Exit(locker);
}
}
退出时不会抛出,但仍然完全错误。
从不锁定变量的内容,然后变异变量; 每个后续锁都会锁定不同的对象!所以你没有互斥。
从不锁定公共对象!如果该列表在任何地方泄漏,则其他错误代码可能以意外顺序锁定该列表,这意味着死锁 - 这是您正在诊断的原始症状。
锁定字段的正确做法是创建一个私有只读对象字段,仅将 用作锁定器,并在每次访问字段时使用。这样你知道(1)字段总是在同一个锁下访问,无论它的值是什么,以及(2)锁对象仅用于锁定该字段,而不是用于锁定其他字段。这确保了互斥并防止死锁。
有人在没有理解关于锁的最基本事实的情况下编写了一个大型多线程程序的事实意味着它几乎肯定是一堆难以发现的错误。在阅读代码时,这一点并不明显,这意味着您没有足够的线程知识来正确解决问题。您需要找到可以帮助您的这方面的专家,或者至少获得正确实践的最低限度的工作知识。
我无法强调这很难。在现代硬件上正确编写具有多个控制线程的程序是非常困难的。您认为语言保证的许多内容仅在单线程程序中得到保证。