我有以下代码:
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会影响引发异常的方式?
答案 0 :(得分:3)
我非常怀疑任何人都可以从你的代码片段中重新解决问题。我的水晶球说你最近改变了程序,最后添加了Thread.Sleep()。并隐藏了原始程序所具有的线程竞争错误。假设呼叫不存在,我会写下这个答案。
仅当所有条件为真时才抛出异常:
res
对象。如果不满足前两个条件,情况就不会如此改变结果的方法是工具&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()
解决了线程争用错误并使结果可预测。好吧,更可预测:)