(这是对该问题的新尝试,现在可以更好地演示该问题。)

假设我们有一个错误的任务(var faultedTask = Task.Run(() => { throw new Exception("test"); });),我们等待它。 await将解压缩AggregateException并引发基础异常。它将抛出faultedTask.Exception.InnerExceptions.First()

根据ThrowForNonSuccess的源代码,它将通过执行大概存储的ExceptionDispatchInfo来执行此操作,以保留良好的堆栈跟踪。如果没有AggregateException,它将不会解压缩ExceptionDispatchInfo

仅凭这个事实就令我感到惊讶,因为文档指出总是会抛出第一个异常:https://msdn.microsoft.com/en-us/library/hh156528.aspx?f=255&MSPPError=-2147217396事实证明await可以抛出AggregateException,但这没有记录的行为。

当我们要创建代理任务并设置其异常(exception)时,这将成为一个问题:

var proxyTcs = new TaskCompletionSource<object>();
proxyTcs.SetException(faultedTask.Exception);
await proxyTcs.Task;

这会抛出AggregateException,而await faultedTask;会抛出测试异常。

如何创建可以随意完成的代理任务,并且可以反射(reflect)原始任务的异常行为?

原始行为是:
  • await将引发第一个内部异常。
  • 所有异常仍然可以通过Task.Exception.InnerExceptions获得。 (此问题的早期版本没有此要求。)

  • 这是一个总结发现的测试:
    [TestMethod]
    public void ExceptionAwait()
    {
        ExceptionAwaitAsync().Wait();
    }
    
    static async Task ExceptionAwaitAsync()
    {
        //Task has multiple exceptions.
        var faultedTask = Task.WhenAll(Task.Run(() => { throw new Exception("test"); }), Task.Run(() => { throw new Exception("test"); }));
    
        try
        {
            await faultedTask;
            Assert.Fail();
        }
        catch (Exception ex)
        {
            Assert.IsTrue(ex.Message == "test"); //Works.
        }
    
        Assert.IsTrue(faultedTask.Exception.InnerExceptions.Count == 2); //Works.
    
        //Both attempts will fail. Uncomment attempt 1 to try the second one.
        await Attempt1(faultedTask);
        await Attempt2(faultedTask);
    }
    
    static async Task Attempt1(Task faultedTask)
    {
        var proxyTcs = new TaskCompletionSource<object>();
        proxyTcs.SetException(faultedTask.Exception);
    
        try
        {
            await proxyTcs.Task;
            Assert.Fail();
        }
        catch (Exception ex)
        {
            Assert.IsTrue(ex.Message == "test"); //Fails.
        }
    }
    
    static async Task Attempt2(Task faultedTask)
    {
        var proxyTcs = new TaskCompletionSource<object>();
        proxyTcs.SetException(faultedTask.Exception.InnerExceptions.First());
    
        try
        {
            await proxyTcs.Task;
            Assert.Fail();
        }
        catch (Exception ex)
        {
            Assert.IsTrue(ex.Message == "test"); //Works.
        }
    
        Assert.IsTrue(proxyTcs.Task.Exception.InnerExceptions.Count == 2); //Fails. Should preserve both exceptions.
    }
    

    这个问题的动机是我试图构造一个将一个任务的结果复制到TaskCompletionSource的函数。这是一个辅助函数,在编写任务组合器函数时经常使用。 API客户端不能检测到原始任务和代理任务之间的差异,这一点很重要。

    最佳答案



    不,这是第一个嵌套异常是AggregateException时的行为。

    基本上,当您调用TaskCompletionSource.SetException(Exception)时,它将异常包装在AggregateException中。

    如果要保留多个异常,只需使用SetException的重载即可,它接受一个IEnumerable<Exception>:

    proxyTcs.SetException(faultedTask.Exception.InnerExceptions);
    

    关于c# - 如何使用TaskCompletionSource.SetException保留等待行为?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/35413686/

    10-13 01:47