今晚我在玩Constrained Execution Regions,以更好地完善我对更精细细节的理解。我以前偶尔使用过它们,但在那些情况下,我大多严格遵守既定模式。无论如何,我注意到一些我无法完全解释的特殊情况。
考虑以下代码。请注意,我以 .NET 4.5 为目标,并在没有附加调试器的情况下使用发布版本对其进行了测试。
public class Program
{
public static void Main(string[] args)
{
bool toggle = false;
bool didfinally = false;
var thread = new Thread(
() =>
{
Console.WriteLine("running");
RuntimeHelpers.PrepareConstrainedRegions();
try
{
while (true)
{
toggle = !toggle;
}
}
finally
{
didfinally = true;
}
});
thread.Start();
Console.WriteLine("sleeping");
Thread.Sleep(1000);
Console.WriteLine("aborting");
thread.Abort();
Console.WriteLine("aborted");
thread.Join();
Console.WriteLine("joined");
Console.WriteLine("didfinally=" + didfinally);
Console.Read();
}
}
你认为这个程序的输出会是什么?
- didfinally=真
- didfinally=假
在你猜测之前阅读文档。我包括下面的相关部分。
受约束的执行区 (CER) 是编写可靠托管代码的机制的一部分。CER 定义了一个区域,其中限制公共语言运行时 (CLR) 引发带外异常,这会阻止该区域中的代码完整执行。在该区域内,用户代码被限制执行会导致抛出带外异常的代码。PrepareConstrainedRegions 方法必须紧接在 try 块之前,并将 catch、finally 和 fault 块标记为受约束的执行区域。一旦标记为受限区域,代码只能调用具有强可靠性契约的其他代码,并且代码不应分配或虚拟调用未准备或不可靠的方法,除非代码准备好处理故障。CLR 延迟在 CER 中执行的代码的线程中止。
和
可靠性 try/catch/finally 是一种异常处理机制,与非托管版本具有相同级别的可预测性保证。catch/finally 块是 CER。块中的方法需要提前准备并且必须是不可中断的。
我现在特别关心的是防止线程中止。有两种:一种是普通Thread.Abort
的通过,另一种是 CLR 主机可以对你进行所有中世纪的操作并强制中止。finally
块已经Thread.Abort
在某种程度上受到保护。然后,如果您将该finally
块声明为 CER,那么您还将获得 CLR 主机中止的额外保护……至少我认为这是理论。
因此,根据我认为我所知道的,我猜到了#1。它应该打印 didfinally=True。当ThreadAbortException
代码仍在try
块中时被注入,然后 CLR 允许finally
块按预期运行,即使没有 CER,对吗?
好吧,这不是我得到的结果。我得到了一个完全出乎意料的结果。#1或#2都没有发生在我身上。相反,我的程序挂在Thread.Abort
. 这是我观察到的。
PrepareConstrainedRegions
延迟线程的存在会在try
块内中止。- 没有
PrepareConstrainedRegions
允许它们在try
块中。
所以百万美元的问题是为什么?该文档在我能看到的任何地方都没有提到这种行为。事实上,我正在阅读的大部分内容实际上是建议您将关键的不可中断代码放在finally
块中,专门用于防止线程中止。
也许,除了块之外,还会PrepareConstrainedRegions
延迟一个块中的正常中止。但是 CLR 主机中止仅在CER 块中延迟?谁能提供更清楚的说明?try
finally
finally