由于您没有 catch 块,因此无法保证 finally 将被执行。来自MSDN - try-finally (C# Reference) 和"Locks and exceptions do not mix" (Eric Lippert)
在处理的异常中,保证关联的 finally 块
要运行。但是,如果异常未处理,则执行
finally 块取决于异常展开操作的方式
触发。反过来,这取决于您的计算机的设置方式。
从随后提到的链接 (Unhandled Exception Processing In The CLR) 中,有各种考虑因素可能意味着您最终会终止线程。不过,老实说,我不知道这是否会让你在 lock 对象上留下一个锁。
如果您想确保:
- 你释放了锁;但是
- 您不想在此级别处理异常,而是希望由更高级别的异常处理程序处理它
然后做:
TheXmlType xml = null;
Monitor.Enter(Lock);
bool inLock = true;
try {
...
xml = filter.Xml; // put this here in case it throws an exception
inLock = false; // set this here in case monitor.exit is to
// throw an exception so we don't do the same all over again in the catch block
Monitor.Exit(Lock);
return xml; // this is fine here, but i would normally put it outside my try
}
catch (Exception) {
if (inLock) Monitor.Exit(Lock);
throw;
}
但是,请注意:不要使用catch (Exception) 隐藏异常,这只有在您重新抛出异常时才可以。人们还建议您使用单个 return 语句,这通常会在您的 try 块之外。
编辑:
通过测试程序确认,来自MSDN - Exceptions in Managed Threads
从通用语言 .NET Framework 2.0 版开始
运行时允许线程中大多数未处理的异常继续进行
自然。在大多数情况下,这意味着未处理的异常
导致应用程序终止。
因此,如果您不处理异常,您的应用程序将会崩溃(您不必担心锁定问题)。如果您确实处理了它,那么您的原始代码将在 finally 块中执行它,您就可以了。
编辑 2: 测试代码已更新,因为它没有正确说明最终未触发:
class Program
{
static void Main(string[] args) {
Program p =new Program();
p.Start();
Console.WriteLine("done, press enter to finish");
Console.ReadLine();
}
private readonly object SyncRoot = new object();
ManualResetEvent mre = new ManualResetEvent(false);
private void Start() {
/*
* The application will run the thread, which throws an exception
* While Windows kicks in to deal with it and terminate the app, we still get
* a couple of "Failed to lock" messages
* */
Thread t1 = new Thread(SetLockAndTerminate);
t1.Start();
mre.WaitOne();
for (int i = 0; i < 10; i++) {
if (!Monitor.TryEnter(this.SyncRoot, 1000)) {
Console.WriteLine("Failed to lock");
}
else {
Console.WriteLine("lock succeeded");
return;
}
}
Console.WriteLine("FINALLY NOT CALLED");
}
public int CauseAnOverflow(int i)
{
return CauseAnOverflow(i + 1);
}
public void SetLockAndTerminate() {
Monitor.Enter(this.SyncRoot);
Console.WriteLine("Entered");
try {
mre.Set();
CauseAnOverflow(1); // Cause a stack overflow, prevents finally firing
}
finally {
Console.WriteLine("Exiting");
Monitor.Exit(this.SyncRoot);
}
}
}