4

使用 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>

但结果仍然是:

在此处输入图像描述

问题:

为什么我没有看到异常?

值得一提的是,我确实在调试模式下看到了异常:

在此处输入图像描述

4

2 回答 2

4

您不能期望这段代码抛出异常,因为子句中的所有语句try所做的只是描述了一个延续模式。StartNewandContinueWith方法不会抛出异常。try所以在后台任务中抛出异常时,代码执行早就离开了这条语句。当程序启动后大约 2 秒后抛出异常时,该Console.Readline语句的执行将停止。

正如您已经发现的那样,您需要等待任务完成才能访问异常或继续本身。

现在,您的应用程序不会因未观察到的异常而死亡的原因是没有发生垃圾收集。当任务被垃圾收集时,未观察到的异常将破坏您的应用程序域。但是当你强制 GC 时,还没有抛出异常,任务也没有完成,所以它不符合垃圾收集的条件。

确保在方法之后GC.Collect拨打电话,然后是另一个:Console.ReadLineConsole.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"/>
于 2016-01-09T15:53:25.783 回答
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);
}
于 2016-01-09T15:53:32.530 回答