ContinueWith 没有捕获到异常?
Exceptions are not caught with ContinueWith?
使用 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>
但结果仍然是:
问题:
为什么我看不到异常?
值得一提的是,我确实在调试模式下看到了异常:
您不能指望这段代码会抛出异常,因为 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"/>
你试过连接到 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);
}
使用 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>
但结果仍然是:
问题:
为什么我看不到异常?
值得一提的是,我确实在调试模式下看到了异常:
您不能指望这段代码会抛出异常,因为 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"/>
你试过连接到 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);
}