是的,有可能有一场比赛using陈述。 C# 编译器转换
using (var obj = new Foo()) {
// statements
}
to:
var obj = new Foo();
try {
// statements
}
finally {
if (obj != null) obj.Dispose();
}
当线程在以下时间之间中止时,就会发生竞争:obj赋值语句和 try 块。几率极小,但不为零。发生这种情况时,该对象不会被处置。请注意他如何通过将赋值移动到 try 块内来重写该代码,以便不会发生这种竞争。当比赛发生时,实际上没有什么根本性的错误,不需要处理物体。
必须在使线程中止稍微提高效率和编写之间做出选择using手动声明时,您应该首先选择不养成使用 Thread.Abort() 的习惯。我不建议实际这样做,using语句有额外的安全措施来确保意外不会发生,它确保即使在 using 语句内重新分配对象时原始对象也会被释放。添加 catch 子句也不太容易发生事故。这using声明存在于reduce出现错误的可能性,请始终使用它。
稍微思考一下这个问题,答案很流行,还有另一个常见的 C# 语句也遭受完全相同的竞争。它看起来像这样:
lock (obj) {
// statements
}
翻译为:
Monitor.Enter(obj);
// <=== Eeeek!
try {
// statements
}
finally {
Monitor.Exit(obj);
}
完全相同的场景,线程中止可以在 Enter() 调用之后和进入 try 块之前发生。这会阻止进行 Exit() 调用。这是way当然,这比不进行的 Dispose() 调用更糟糕,这几乎肯定会导致死锁。该问题特定于 x64 抖动,详细描述如下乔·达菲博客文章 https://web.archive.org/web/20120611083524/http://www.bluebytesoftware.com/blog/2007/01/30/MonitorEnterThreadAbortsAndOrphanedLocks.aspx.
可靠地修复这个问题非常困难,将 Enter() 调用移到 try 块内并不能解决问题。您无法确定是否进行了 Enter 调用,因此无法可靠地调用 Exit() 方法而不可能触发异常。 Duffy 所说的 Monitor.ReliableEnter() 方法最终确实发生了。 .NET 4 版本的 Monitor 具有 TryEnter() 重载,该重载需要ref bool lockTaken
争论。现在您知道可以调用 Exit() 了。
好吧,当你不注意的时候,可怕的东西会在晚上突然出现。编写可安全中断的代码是hard。明智的做法是永远不要假设您没有编写的代码已经解决了所有这些问题。测试这样的代码非常困难,因为竞争非常罕见。你永远无法确定。