Console.WriteLine如何影响异常堆栈

时间:2015-09-18 10:23:51

标签: c# c#-4.0 asynchronous

我有以下代码:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Started");
        var res = GetSlowStringAsync();

        res.ContinueWith(
            c =>
                {
                    Console.WriteLine("Will it crash?");
                    //Console.WriteLine(c.Result);
                }
            );

        //Console.WriteLine("Press any key");
        Console.ReadKey();
        Console.WriteLine("Continue on the main thread");

        GC.Collect();
        Console.WriteLine("Memory cleared");

        Thread.Sleep(10000);
    }

    public static async Task<string> GetSlowStringAsync()
    {
        await Task.Delay(2000);
        throw new Exception("will you handle it?");
        return "somestring";
    }
}

同样在App.config中我添加了以下几行:

<runtime>
    <ThrowUnobservedTaskExceptions enabled ="true" />
</runtime>

我正在使用visual studio 2015 14.0.23107.0 D14REL 目标.Net Framework 4.6。 执行&#34;启动而不调试&#34;模式。

如果经过问题&#34;它会崩溃&#34;按任意按钮,然后程序将崩溃。

但是如果要取消注释

Console.WriteLine("Press any key");

并执行&#34;无需调试即可运行&#34;模式然后程序不会崩溃。为什么Console.WriteLine会影响引发异常的方式?

1 个答案:

答案 0 :(得分:3)

我非常怀疑任何人都可以从你的代码片段中重新解决问题。我的水晶球说你最近改变了程序,最后添加了Thread.Sleep()。并隐藏了原始程序所具有的线程竞争错误。假设呼叫不存在,我会写下这个答案。

仅当所有条件为真时才抛出异常:

  • 您运行程序的发布版本
  • 在没有附加调试器的情况下运行程序
  • GC.Collect()调用实际上是垃圾收集res对象。如果不满足前两个条件,情况就不会如此
  • 在main()方法结束之前GC.Collect()完成后,终结器线程有足够的时间完成其工作。 Console.WriteLine()调用可以给它足够的时间来运行TaskExceptionHolder终结器。不是保证,线程争用的问题。

改变结果的方法是工具&gt;选项&gt;调试&gt;一般&gt;取消勾选“抑制JIT优化”选项。这可以确保连接调试器不会影响结果。并在GC.Collect()调用之前添加res = null;或将代码移动到另一个方法中,以确保即使在Debug构建中也可以收集res。为什么GC.Collect()的行为如此不可预测,在this post中进行了解释。

要了解行为,您需要知道的唯一其他事情是,当程序忙于关闭时,会禁止异常。请注意在TaskExceptionHolder finalizer中使用Environment.HasShutdownStarted和AppDomain.CurrentDomain.IsFinalizingForUnload属性。因此,如果终结器线程在执行其作业时有点慢,那么在终结器运行时可能已经开始关闭,并且您将不会获得异常。在GC.Collect()调用之后添加到Main()的任何内容(如Console.WriteLine())都会提高您看到异常的几率。

添加GC.WaitForPendingFinalizers()解决了线程争用错误并使结果可预测。好吧,更可预测:)