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;
...
}
'Programing' 카테고리의 다른 글
G1에 대한 Java 7 (JDK 7) 가비지 콜렉션 및 문서 (0) | 2020.10.08 |
---|---|
배경 Drawable을 설정할 때 패딩은 어디에 있습니까? (0) | 2020.10.08 |
파이썬에서 상속의 요점은 무엇입니까? (0) | 2020.10.08 |
Winforms TableLayoutPanel 프로그래밍 방식으로 행 추가 (0) | 2020.10.08 |
역 추적과 깊이 우선 검색의 차이점은 무엇입니까? (0) | 2020.10.08 |