使用Visual Studio 2015,定位FW 4(在FW 4下测试不可观察的异常):
我期待这段代码:
static void Main(string[] args)
{
try
{
Task.Factory.StartNew(() => Console.WriteLine(1))
.ContinueWith(t => Thread.Sleep(1000))
.ContinueWith(t => Console.WriteLine(2))
.ContinueWith(t => Thread.Sleep(1000))
.ContinueWith(t => { throw new Exception("aaaa"); })
.ContinueWith(t => Console.WriteLine(3));
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
GC.Collect();
GC.Collect();
Console.ReadLine();
}
向我展示例外。
我知道我可以通过T.Wait()
或t.Exception
的上一个任务看到它 - 但为什么我没有在这里看到异常?
我知道4.5中的异常处理机制是已更改,为了获得我应该添加的旧机制:
<ThrowUnobservedTaskExceptions enabled="true"/>
我做了什么:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
</startup>
<runtime>
<ThrowUnobservedTaskExceptions enabled="true"/>
</runtime>
</configuration>
但结果却是:
问题:
为什么我没有看到异常?
值得一提的是我确实在调试模式中看到了异常:
答案 0 :(得分:4)
您不能指望此代码抛出异常,因为try
子句中的所有语句都描述了延续模式。 StartNew
和ContinueWith
方法不会抛出异常。因此,当抛出后台任务中的异常时,代码执行很久就会留下这个try
语句。在程序启动后大约2秒后抛出异常时,Console.Readline
语句将暂停执行。
正如您已经发现的那样,您需要等待任务完成才能访问异常或继续本身。
现在你的应用程序没有因未观察到的异常而死的原因是因为没有发生垃圾收集。当任务被垃圾收集时,未观察到的异常将拆除您的应用程序域。但是当你强制使用GC时,异常还没有被抛出,任务都没有完成,所以它没有资格进行垃圾收集。
确保您在GC.Collect
方法之后发出Console.ReadLine
来电后跟另一个Console.ReadLine
:
Task.Factory.StartNew(() => Console.WriteLine(1))
.ContinueWith(t => Thread.Sleep(1000))
.ContinueWith(t => Console.WriteLine(2))
.ContinueWith(t => Thread.Sleep(1000))
.ContinueWith(t => { throw new Exception("aaaa"); })
.ContinueWith(t => Console.WriteLine(3));
Console.ReadLine();
GC.Collect();
GC.Collect();
Console.ReadLine();
显然应该存在以下配置开关:
<ThrowUnobservedTaskExceptions enabled="true"/>
答案 1 :(得分:0)
你有没有尝试连接TaskScheduler.UnobservedTaskException
是否被抓到了?
[HandleProcessCorruptedStateExceptions]
public static void SetupUnobservedTaskExceptionHandling(ILogger logger)
{
logger.Debug("Setting up unobserved task exception handling...");
TaskScheduler.UnobservedTaskException += (sender, args) =>
{
var e = args.Exception;
logger.ErrorFormat("TaskScheduler Unobserved Exception - Message: {0}", e.Message);
logger.ErrorFormat("TaskScheduler Unobserved Exception - Inner exception: {0}", e.InnerException);
logger.ErrorFormat("TaskScheduler Unobserved Exception - Inner exceptions: {0}", e.InnerExceptions);
logger.ErrorFormat("TaskScheduler Unobserved Exception - StackTrace: {0}", e.StackTrace);
args.SetObserved();
};
}
如果你想从任务本身处理它,你也可以使用类似的东西:
/// <summary>
/// Handles all the exceptions thrown by the <paramref name="task"/>
/// </summary>
/// <param name="task">The task which might throw exceptions.</param>
/// <param name="exceptionsHandler">The handler to which every exception is passed.</param>
/// <returns>The continuation task added to <paramref name="task"/>.</returns>
public static Task HandleExceptions(this Task task, Action<Exception> exceptionsHandler)
{
return task.ContinueWith(t =>
{
var e = t.Exception;
if (e == null) { return; }
e.Flatten().Handle(ie =>
{
exceptionsHandler(ie);
return true;
});
},
CancellationToken.None,
TaskContinuationOptions.ExecuteSynchronously,
TaskScheduler.Default);
}