【问题标题】:.NET multi-thread and exceptions.NET 多线程和异常
【发布时间】:2013-06-21 15:43:55
【问题描述】:

我写了这段代码sn-p:

        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 方法。

我在调试和不调试的情况下都运行了它,行为是一样的。

任何想法我做错了什么?

【问题讨论】:

  • 你如何“看到线程正在中断主线程”?
  • the child thread terminates silently - 你是从哪里看到这个的?
  • ...在调试和不运行的情况下运行它,并且行为是相同的...通过调试,您可以在 Visual Studio 中看到它。你在没有调试的情况下看到了什么?只需双击 exe 就会使 exe 崩溃并出现程序崩溃对话框。 是如何在没有调试的情况下得到断点的?
  • 反向 - 1) 没有调试,应用程序崩溃并显示消息 - 没有断点。 2) 子线程终止 - 这不是 .NET 运行时的行为吗?你是说子线程可以终止父线程? 3)我看到 VS2012 中断(调试)或应用程序崩溃(非调试)。 4) 不是重复。

标签: .net multithreading exception-handling


【解决方案1】:

自 .NET 2.0 起,未处理的异常会终止程序。认为主线程被“中断”并不是正确的思维方式,整个程序被中止,所有线程都死掉了。这是 Thread.Abort() 的“粗鲁”版本,无法停止。

AppDomain.UnhandledException 事件发生了最后的喘息。它触发让您有机会记录 e.UnhandledException.ToString() 的值,以便您可以诊断崩溃。顺便说一句,经常被忽视,但是当您的程序在野外出现并且用户和他们的机器以您没有想到的通常非常令人惊讶的方式对待您的程序时,处理崩溃是必不可少的。

使用 .config 文件中覆盖默认 CLR 策略的属性实际上可以不使程序崩溃。但是这种方式很疯狂,线程在没有完成工作的情况下终止只会导致程序以完全无法诊断的方式行为异常。在 .NET 1.x 中尝试过,但作为一个坏主意被拒绝。

【讨论】:

    【解决方案2】:

    你看到的是正确的行为。用户启动的任何线程中未处理的异常都会导致程序崩溃。

    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 Task Parallel Library 和 C# 5.0 async/await 了解一些 .NET/C# 的精彩之处。

    【讨论】:

      【解决方案3】:

      你的理解几乎是正确的。

      线程中任何未处理的异常(ThreadAbortException 除外)都会在调用堆栈中有效地破坏它。然后线程被默默地杀死。但灾难并不止于此。然后异常继续消耗整个运行过程。但就在进程被终止之前,会引发AppDomain.UnhandledException 事件。它使您有机会记录异常(或发出很抱歉的消息)。 UnhanledExceptionEventArgsisTerminating 属性始终为 true

      需要注意的是,在dot-net中,线程之间没有父子关系。所有线程都是平等的。唯一线程之下的是 AppDomain 本身。

      现在解释一下你所看到的,第二个线程(不是子线程)中的异常并没有中断或影响第一个线程。第二个线程被杀死。然后整个过程被杀死。一旦进程被杀死,第一个线程就无处可藏。它注定要死。现在,如果代码在 Visual Studio 中运行(附加了调试器),调试器一旦意识到没有 catch 块可以捕获它,就可以捕获异常。那就是当你看到它进入主线程的时候。调试器不只是中断主线程,它正在暂停所有线程,包括发生异常的线程。它使您有机会修改代码并恢复它。

      没有调试器,异常会吃掉自己的线程,然后消耗整个进程。

      【讨论】:

      • 实际上吃和消费要少得多。当 CLR 检测到未处理的异常时,它会关闭。正如您所建议的那样,线程/进程不会发生任何破坏性事件。
      • 感谢所有答案 - 如果我能将所有 3 个标记为正确,我会的。
      【解决方案4】:

      未处理的异常会产生如下不同的结果。

      1. 如果使用 Thread 类显式运行线程,则任何未处理的异常都会导致应用程序关闭。
      2. 如果使用线程池运行线程(使用 TPL、ThreadQueueWorker、任何其他方式) 一种。如果使用任务非泛型对象,则不会导致应用程序崩溃 湾。如果使用任务,则会导致应用程序崩溃。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-12-16
        • 2010-10-14
        • 1970-01-01
        • 2011-07-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多