由于您没有 catch 块,因此无法保证 finally 将被执行。来自MSDN - try-finally(C# 参考)和“锁和异常不混合”(Eric Lippert)
在已处理的异常中,保证运行关联的 finally 块。但是,如果异常未处理,finally 块的执行取决于异常展开操作的触发方式。反过来,这取决于您的计算机的设置方式。
然后从提到的链接(CLR 中的未处理异常处理)中,有各种考虑因素可能意味着您最终会得到一个终止的线程。不过,老实说,我不知道这是否会让您锁定锁定对象。
如果您想确保:
- 你释放锁;但
- 您不想在此级别处理异常,而是希望它由更高级别的异常处理程序处理
然后做:
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 确认 - 托管线程中的异常
从 .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);
}
}
}