Programing

Task.WhenAll에서 AggregateException이 발생하는 이유는 무엇입니까?

crosscheck 2020. 10. 8. 07:43
반응형

Task.WhenAll에서 AggregateException이 발생하는 이유는 무엇입니까?


이 코드에서 :

private async void button1_Click(object sender, EventArgs e) {
    try {
        await Task.WhenAll(DoLongThingAsyncEx1(), DoLongThingAsyncEx2());
    }
    catch (Exception ex) {
        // Expect AggregateException, but got InvalidTimeZoneException
    }
}

Task DoLongThingAsyncEx1() {
    return Task.Run(() => { throw new InvalidTimeZoneException(); });
}

Task DoLongThingAsyncEx2() {
    return Task.Run(() => { throw new InvalidOperation();});
}

기다리고 있던 작업 중 하나 이상에서 예외가 발생했기 때문에 WhenAll를 만들고 던질 것으로 예상 했습니다 AggregateException. 대신 작업 중 하나에서 발생한 단일 예외가 반환됩니다.

않습니다 WhenAll항상을 만들 수 없습니다 AggregateException?


나는 정확히 어디에 있는지 기억하지 못하지만 새로운 async / await 키워드 AggregateException를 사용하여 실제 예외로 풀린다는 것을 읽었습니다 .

따라서 catch 블록에서 집계 된 예외가 아닌 실제 예외가 발생합니다. 이를 통해보다 자연스럽고 직관적 인 코드를 작성할 수 있습니다.

이는 또한 많은 코드가 집계 된 예외가 아닌 특정 예외를 예상하는 경우 기존 코드를 async / await 사용으로 쉽게 변환하는 데 필요했습니다 .

-- 편집하다 --

알았다:

Bill Wagner의 비동기 입문서

Bill Wagner는 다음과 같이 말했습니다 : ( 예외 발생시 )

... await를 사용하면 컴파일러에서 생성 된 코드가 AggregateException을 풀고 기본 예외를 throw합니다. await를 활용하면 Task.Result, Task.Wait 및 Task 클래스에 정의 된 기타 Wait 메서드에서 사용하는 AggregateException 유형을 처리하기위한 추가 작업을 피할 수 있습니다. 이것이 기본 Task 메서드 대신 await를 사용하는 또 다른 이유입니다 ....


나는 이것이 이미 답변 된 질문이라는 것을 알고 있지만 선택한 답변은 실제로 OP의 문제를 해결 하지 못 하므로 이것을 게시 할 것이라고 생각했습니다.

이 솔루션은 집계 예외 (즉 , 다양한 작업에서 발생한 모든 예외)를 제공하고 차단하지 않습니다 (워크 플로는 여전히 비동기적임).

async Task Main()
{
    var task = Task.WhenAll(A(), B());

    try
    {
        var results = await task;
        Console.WriteLine(results);
    }
    catch (Exception)
    {
    }

    if (task.Exception != null)
    {
        throw task.Exception;
    }
}

public async Task<int> A()
{
    await Task.Delay(100);
    throw new Exception("A");
}

public async Task<int> B()
{
    await Task.Delay(100);
    throw new Exception("B");
}

핵심은 집계 작업에 대한 참조를 대기하기 전에 저장 한 다음 AggregateException을 보유하는 Exception 속성에 액세스 할 수 있습니다 (단 하나의 작업에서만 예외가 발생하더라도).

이것이 여전히 유용하기를 바랍니다. 나는 오늘이 문제가 있다는 것을 알고 있습니다.


모든 작업을 탐색하여 둘 이상의 작업에서 예외가 발생했는지 확인할 수 있습니다.

private async Task Example()
{
    var tasks = new [] { DoLongThingAsyncEx1(), DoLongThingAsyncEx2() };

    try 
    {
        await Task.WhenAll(tasks);
    }
    catch (Exception ex) 
    {
        var exceptions = tasks.Where(t => t.Exception != null)
                              .Select(t => t.Exception);
    }
}

private Task DoLongThingAsyncEx1()
{
    return Task.Run(() => { throw new InvalidTimeZoneException(); });
}

private Task DoLongThingAsyncEx2()
{
    return Task.Run(() => { throw new InvalidOperationException(); });
}

당신은 생각하고 있습니다 Task.WaitAll-그것은 AggregateException.

WhenAll just throws the first exception of the list of exceptions it encounters.


Just thought I'd expand on @Richiban's answer to say that you can also handle the AggregateException in the catch block by referencing it from the task. E.g:

async Task Main()
{
    var task = Task.WhenAll(A(), B());

    try
    {
        var results = await task;
        Console.WriteLine(results);
    }
    catch (Exception ex)
    {
        // This doesn't fire until both tasks
        // are complete. I.e. so after 10 seconds
        // as per the second delay

        // The ex in this instance is the first
        // exception thrown, i.e. "A".
        var firstExceptionThrown = ex;

        // This aggregate contains both "A" and "B".
        var aggregateException = task.Exception;
    }
}

public async Task<int> A()
{
    await Task.Delay(100);
    throw new Exception("A");
}

public async Task<int> B()
{
    // Extra delay to make it clear that the await
    // waits for all tasks to complete, including
    // waiting for this exception.
    await Task.Delay(10000);
    throw new Exception("B");
}

In your code, the first exception is returned by design as explained at http://blogs.msdn.com/b/pfxteam/archive/2011/09/28/task-exception-handling-in-net-4-5.aspx

As for your question, you will get the AggreateException if you write code like this:

try {
    var result = Task.WhenAll(DoLongThingAsyncEx1(), DoLongThingAsyncEx2()).Result; 
}
catch (Exception ex) {
    // Expect AggregateException here
} 

This works for me

private async Task WhenAllWithExceptions(params Task[] tasks)
{
    var result = await Task.WhenAll(tasks);
    if (result.IsFaulted)
    {
                throw result.Exception;
    }
}

Try this code pattern:

Task task = null;
try
{
    task = Task.WhenAll(...);
    await task;
 }
 catch (AggregateException aggException)
 {
     var aggException = task.Exception;
     ...
 }

참고URL : https://stackoverflow.com/questions/12007781/why-doesnt-await-on-task-whenall-throw-an-aggregateexception

반응형