我读过的所有内容声称线程中止将在从ThreadAbortException结束之前执行finally块。我想确认这一点,以便我可以计划如何处理可以无限期挂起的某些第三方代码。然而,以下测试让我感到困惑:
public void runTest(DateTime deadline)
{
testThread = new Thread(() =>
{
try
{
Console.WriteLine("test thread started at " + DateTime.Now.ToShortTimeString());
while (true) { }
}
finally
{
Console.WriteLine("test thread entered FINALLY at " + DateTime.Now.ToShortTimeString());
while (true) { }
}
});
testThread.Start();
while (testThread.IsAlive && deadline.Subtract(DateTime.Now).TotalSeconds > 0)
{
Console.WriteLine("main thread while loop " + DateTime.Now.ToShortTimeString());
Thread.Sleep(10000);
}
if (testThread.IsAlive)
testThread.Abort();
Console.WriteLine("main thread after abort call " + DateTime.Now.ToShortTimeString());
}
我在运行时发现的是控制台从未提及进入finally块。应用程序在.abort调用之后继续,好像根本没有finally块。难道我做错了什么?在到达最终写入控制台之前,不应该控制传递给finally块,还是执行顺序仍然是最终处于单独线程中的事实的函数?
答案 0 :(得分:7)
Docs say:ThreadAbortException
是一个可以捕获的特殊异常,但它会在catch块的末尾自动再次引发。引发此异常时,运行时会在结束线程之前执行所有finally块。因为线程可以在finally块中执行无限制计算或调用Thread.ResetAbort来取消中止,所以无法保证线程将永远结束。
我非常确定您的线程正在被转储,因为您退出方法并丢失对它的引用,因此它被垃圾收集器收集。尝试将testThread变量作为类的字段成员,看看会发生什么。
那,或者你有一个竞争条件,因为线程并行运行:主线程在spun-up测试线程可以输出finally数据之前完成(例外是昂贵的,需要时间来达到catch或finally块)。
答案 1 :(得分:5)
工作线程函数中的finally块在与主线程并行的工作线程上执行。这是竞争条件。你不能告诉终止哪个工作线程或者在中止调用之后更快地执行主线程代码。如果你需要同步中止,那么你必须这样做:
if (testThread.IsAlive)
{
testThread.Abort();
bool blnFinishedAfterAbort = testThread.Join(TimeSpan.FromMilliseconds(1000));
if (!blnFinishedAfterAbort)
{
Console.WriteLine("Thread abort failed.");
}
}
Console.WriteLine("main thread after abort call " + DateTime.Now.ToShortTimeString());
请记住,如果启用了遗留异常处理(请参阅http://msdn.microsoft.com/en-us/library/ms228965.aspx)并指定了 AppDomain_UnahandledException 事件处理程序,则ThreadAbortException将导致执行该处理程序之前最后在工作线程函数中阻止。这只是在中止线程时应该注意的令人沮丧和意外的执行顺序的另一个例子。
答案 2 :(得分:2)
通常不应跳过finally
。
控制台应用程序(假设它是一个)可能会在finally
块运行之前退出(因为在您调用Thread.Abort()
后没有等待)。
如果您在程序结尾处放置Console.ReadLine()
权限会怎样?