【问题标题】:.NET Core: Finally block not called on unhandled exception on Linux.NET Core:Linux 上未处理的异常未调用 finally 块
【发布时间】:2017-10-17 09:25:56
【问题描述】:

我创建了以下 C# 程序:

namespace dispose_test
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var disp = new MyDisposable())
            {
                throw new Exception("Boom");
            }
        }
    }

    public class MyDisposable : IDisposable
    {
        public void Dispose()
        {
            Console.WriteLine("Disposed");
        }
    }
}

当我使用 dotnet run 运行它时,我看到以下行为:

  • Windows:异常文本写入控制台,大约 20 秒后打印“Disposed”,程序退出。
  • Linux:异常文本写入控制台,程序立即退出。 “处置”从未写过。

Windows 上的延迟很烦人,但在 Linux 上根本不调用 Dispose() 的事实令人不安。这是预期的行为吗?

编辑以下对话中的澄清/补充:

  • 这不是using/Dispose()特有的,它只是try/finally的一个特例。该行为通常也发生在try/finally - finally 块未运行。我已更新标题以反映这一点。
  • 我还通过将文件写入文件系统来检查Dispose() 的执行,只是为了确保问题与在未处理的情况下运行Dispose() 之前从控制台断开标准输出无关例外。行为相同。
  • 如果在应用程序中的任何地方捕获到异常,Dispose() 就会被调用。当应用程序完全未处理它时,就会发生这种行为。
  • 在 Windows 上,长间隔不是由于编译延迟。当异常文本写入控制台时,我开始计时。
  • 我最初的实验是在两个平台上做dotnet run,这意味着单独编译,但我也尝试在Windows上做dotnet publish并直接在两个平台上运行输出,结果相同。唯一的区别是,直接在 Linux 上运行时,文本“Aborted (core dumped)”写在异常文本之后。

版本详情:

  • dotnet --version -> 1.0.4.
  • 编译为 netcoreapp1.1,在 .NET Core 1.1 上运行。
  • lsb-release -d -> Ubuntu 16.04.1 LTS

【问题讨论】:

  • 你应该在异常之前写一些东西到控制台,看看这20秒的延迟是不是编译延迟。
  • 您确定没有调用Dispose 吗?难道是它被调用但stdout 已经分离?您可以尝试将Console.WriteLine 更改为File.WriteAllText 并查看是否写入了文件。
  • @FedericoDipuma 是的,也试过了,结果相同——文件是在 Windows 上写入的,而不是在 Linux 上。
  • 如果你明确写出using 会发生什么,即使用try/finally?如果添加catch 以便实际上处理异常会发生什么?
  • 听起来像是一个应该报告的错误(例如,通过 Connect 或 GitHub 问题)。根据您的描述,即使存在finally 子句,似乎一个未处理的异常也会在不展开堆栈的情况下破坏进程。我认为这实际上与IDisposable 的关系不大,而与finally 子句的执行不足有关,无论它们可能包含什么。

标签: c# linux .net-core


【解决方案1】:

Official response 是这是一个预期的行为。

有趣的是,C# doc page on try-finally 在顶部明确指出了这一点(强调我的)

在处理的异常中,保证运行相关的 finally 块。 但是,如果异常未处理,finally 块的执行取决于异常展开操作的触发方式。这又取决于计算机的设置方式。有关详细信息,请参阅 CLR 中的未处理异常处理。

通常,当未处理的异常结束应用程序时,finally 块是否运行并不重要。但是,如果即使在这种情况下也必须在 finally 块中运行语句,则一种解决方案是在 try-finally 语句中添加一个 catch 块。或者,您可以捕获调用堆栈更高的 try-finally 语句的 try 块中可能引发的异常。也就是说,您可以在调用包含 try-finally 语句的方法的方法中,或在调用该方法的方法中,或在调用堆栈中的任何方法中捕获异常。 如果没有捕获到异常,finally块的执行取决于操作系统是否选择触发异常展开操作。

我在实验中发现的一件事是,它似乎不足以捕获异常,您还必须处理它。如果执行通过throw 离开catch 块,finally 将不会运行。

【讨论】:

    【解决方案2】:

    如果你用 try-catch 包围它,当异常被外部 catch 捕获(处理)时,finally 块将运行。

        static void Main(string[] args)
        {
            try
            {
                using (var disp = new MyDisposable())
                {
                    throw new Exception("Boom");
                }
            }
            catch (Exception e)
            {
                throw;
            }
        }
    

    【讨论】:

    • 是的,问题范围仅限于未处理的异常。
    猜你喜欢
    • 2015-01-17
    • 2011-08-26
    • 2016-09-19
    • 1970-01-01
    • 2011-11-25
    • 1970-01-01
    • 2019-03-31
    • 2019-09-12
    • 2011-12-20
    相关资源
    最近更新 更多