(这是对该问题的新尝试,现在可以更好地演示该问题。)
假设我们有一个错误的任务(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/