我写了这段代码:
static void Main(string[] args)
{
Console.WriteLine("Start");
Thread secondThread = new Thread(ThrowAnException);
secondThread.Start();
Console.ReadKey();
}
static void ThrowAnException()
{
throw new Exception("Second Thread Exception");
}
}
我的理解是,当异常发生在第二个线程上时,异常会向下移动线程的堆栈,如果未处理,子线程将以静默方式终止。我所看到的是该线程正在中断主线程并在ThrowAnException方法中打破“Exception was Unhandled”。
我通过调试和没有调试来运行它,行为是相同的。
任何想法我做错了什么?
答案 0 :(得分:7)
自.NET 2.0以来,未处理的异常终止程序。认为主线程被“中断”不是正确的思维模式,整个程序被中止并且所有线程死亡。它是Thread.Abort()的“粗鲁”版本,无法停止。
通过AppDomain.UnhandledException事件最后一次喘息。它会激活你,让你有机会记录e.UnhandledException.ToString()的值,这样你就可以有机会诊断崩溃。经常被忽视,但是当你的程序在野外出现时,处理崩溃是必不可少的,用户和他们的机器通常会以非常的方式处理你的程序。
实际上,可能不会使程序崩溃,而.config文件中的属性会覆盖默认的CLR策略。但这种方式就是疯狂,在没有完成工作的情况下终止的线程只会导致程序以完全不可识别的方式行为不端。在.NET 1.x中尝试并被拒绝为坏主意。
答案 1 :(得分:1)
你看到的是正确的行为。用户启动的任何线程中的未处理异常都会导致程序崩溃。
the child thread terminates silently
可能是,也许你所描述的是来自.NET 1.x的古代。从.NET 2.0及更高版本开始不再适用。
所有人都说,有些方法可以使线程不会导致程序崩溃。 (这只是为了演示而不是练习。永远不要这样做。)
Action a = ThrowAnException;
a.BeginInvoke(null, null);
这将导致使用APM在ThreadPool线程上调用ThrowAnException
。在致电EndInvoke
之前,您永远不会看到例外情况。但是,不再建议使用APM进行异步。
看看C#4.0任务并行库和C#5.0异步/等待某些.NET / C#awesomeness。
答案 2 :(得分:0)
您的理解几乎是正确的。
线程中任何未处理的异常(ThreadAbortException
除外)都会在调用堆栈中有效地粉碎它。然后线程被默默杀死。但灾难并没有止步于此。然后异常继续消耗整个运行过程。但是在进程被杀之前,会引发AppDomain.UnhandledException
事件。它为您提供了记录异常的机会(或者提出了一个很遗憾的消息)。 isTerminating
的{{1}}属性始终为UnhanledExceptionEventArgs
。
需要注意的是,在dot-net中,线程之间没有父子关系。所有线程都是平等的。 以下线程唯一的东西就是AppDomain本身。
现在解释一下你看到的内容,第二个线程(非子线程)中的异常没有中断或无论如何影响第一个线程。第二个线程被杀死。然后整个过程被杀死。一旦进程被杀死,第一个线程就无处隐藏。它注定要死。现在,如果代码在visual studio中运行(附加了调试器),调试器一旦意识到没有true
块可以捕获它,就可以捕获异常。那就是当你看到打破主线程时。调试器不只是在主线程中断,而是挂起所有线程,包括发生异常的线程。它让你有机会修改代码并恢复它。
没有调试器,异常会占用自己的线程,然后它会消耗整个过程。
答案 3 :(得分:0)
未处理的异常会产生如下不同的结果。