Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/21.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/image-processing/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何使用TaskCompletionSource.SetException保留等待行为?_C#_.net_Async Await_Task Parallel Library_Taskcompletionsource - Fatal编程技术网

C# 如何使用TaskCompletionSource.SetException保留等待行为?

C# 如何使用TaskCompletionSource.SetException保留等待行为?,c#,.net,async-await,task-parallel-library,taskcompletionsource,C#,.net,Async Await,Task Parallel Library,Taskcompletionsource,(这是对这个问题的新尝试,现在更好地说明了这个问题。) 假设我们有一个出错的任务(var faultedTask=task.Run(()=>{throw new Exception(“test”);})),我们等待它await将解压aggregateeexception并引发底层异常。它将抛出faultedTask.Exception.InnerExceptions.First() 根据ThrowForNonSuccess的源代码,它将通过执行任何存储的ExceptionDispatchInfo

(这是对这个问题的新尝试,现在更好地说明了这个问题。)

假设我们有一个出错的任务(
var faultedTask=task.Run(()=>{throw new Exception(“test”);})
),我们等待它
await
将解压
aggregateeexception
并引发底层异常。它将抛出
faultedTask.Exception.InnerExceptions.First()

根据ThrowForNonSuccess的源代码,它将通过执行任何存储的
ExceptionDispatchInfo
来实现这一点,以保留良好的堆栈跟踪。如果没有
例外DispatchInfo
,它将不会解压缩
聚合异常

这一事实本身就让我感到惊讶,因为文档中说明了第一个异常总是被抛出的:事实证明,
await
可以抛出
aggregateeexception
,但这不是文档化的行为

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

var proxyTcs = new TaskCompletionSource<object>();
proxyTcs.SetException(faultedTask.Exception);
await proxyTcs.Task;
这个问题的动机是我试图构造一个函数,将一个任务的结果复制到
TaskCompletionSource
。这是一个帮助函数,在编写任务组合器函数时经常使用。重要的是,API客户端无法检测原始任务和代理任务之间的差异

事实证明,Wait可以抛出AggregateException,但这并不是有文档记录的行为

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

基本上,当您调用
TaskCompletionSource.SetException(Exception)
时,它会将该异常包装在一个
aggregateeexception

如果要保留多个异常,只需使用
SetException
的重载,它接受
IEnumerable


阅读来自50k+代表用户的新问题总是很有趣的,90%的时间我都会说“哦,我也很想知道”并最终喜欢这个问题如果你想了解异常的
wait
语义,为什么不直接转到源代码()复制任务等待者所做的事情?@StephenCleary mine现在正是本着这种精神进行建模的。同时,我也复习了乔恩的公共守则。显然,除了我,每个人都知道使用正确的过载。正如我刚才测试的那样,让TPL代码的每一个细微差别都正确是一门黑色艺术。更倾向于使用更干净的SetException方法,尽管这两种方法都可以使用堆栈跟踪,但“展开”中有一个无用的部分。
[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.
}
proxyTcs.SetException(faultedTask.Exception.InnerExceptions);