12

오류가 발생한 작업 (예외가 설정된 태스크)을 기다리는 경우,await스토어드 예외를 재발행합니다. 격납 예외가AggregateException그것은 첫 번째를 제물로 바칠 것이고 나머지는 버릴 것입니다.

우리는 어떻게 사용할 수 있습니까?await동시에 원본을 던지십시오.AggregateException실수로 오류 정보를 잃지 않도록하기 위해?

물론이를 위해 해킹 된 솔루션을 생각해 볼 수도 있습니다 (예 :await, 다음 전화Task.Wait). 정말 깨끗한 솔루션을 찾고 싶습니다.가장 좋은 방법은 무엇입니까?

나는 주문 습관을 사용하는 것을 생각했다. 그러나 붙박이의TaskAwaiter완전히 재현 할 방법이 확실치 않은 많은 마법이 있습니다. TPL 유형의 내부 API를 호출합니다. 나는 또한하지 않는다.필요그 모든 것을 재현하는 것.

다음과 같이 연주하고 싶다면 다음과 같이 짧게 준비하십시오 :

static void Main()
{
    Run().Wait();
}

static async Task Run()
{
    Task[] tasks = new[] { CreateTask("ex1"), CreateTask("ex2") };
    await Task.WhenAll(tasks);
}

static Task CreateTask(string message)
{
    return Task.Factory.StartNew(() => { throw new Exception(message); });
}

두 가지 예외 중 하나만 throw됩니다.Run.

스택 오버플로에 대한 다른 질문은이 특정 문제를 해결하지 못합니다. 중복을 제안 할 때주의하십시오.


  • 이 블로그 항목에서 예외 처리를 구현 한 이유를 설명하는 내용을 보았습니까?await그들이 그랬던 것처럼?blogs.msdn.com/b/pfxteam/archive/2011/09/28/10217876.aspx - Matthew Watson
  • 네, 고마워요. 나는 일반적인 경우에 대한 그 추론에 동의하지만, 유스 케이스는 다루지 않습니다. - usr
  • " 모든 경우에 태스크의 Exception 속성은 여전히 모든 예외를 포함하는 AggregateException을 반환하므로 throw 된 것을 잡아서 다시 필요할 경우 Task.Exception과 협의 할 수 있습니다. " - Timbo

4 답변


20

나는 당신의 질문 제목에 함축 된 의미에 동의하지 않습니다.await의 행동은 바람직하지 않습니다. 대다수 시나리오에서 의미가 있습니다. 안에WhenAll상황, 얼마나 자주 당신을합니까정말알 필요가있다모든오류 세부 정보 중 하나와 반대되는 부분?

주요 어려움AggregateException는 예외 처리입니다. 즉, 특정 유형을 포착 할 수있는 능력을 잃게됩니다.

즉, 확장 메소드를 사용하여 원하는 동작을 얻는 것은 쉽습니다.

public static async Task WithAggregateException(this Task source)
{
    try
    {
        await source.ConfigureAwait(false);
    }
    catch
    {
        throw source.Exception;
    }
}


  • 모든 예외를 얻는 것은 진단에 중요합니다. 나는 nullrefs를 놓치고 싶지 않습니다. 로그되어야합니다.손질그러나 예외는 다른 시나리오입니다. Btw, 솔루션을 잘 작동합니다. 감사. - usr
  • 얘기하고 있다면치명적 또는 골수 예외, 최상위 수준 핸들러를 사용하는 것이 좋습니다 (UnobservedTaskException, 응용 프로그램 처리기)try/catch블록. - Stephen Cleary
  • 나는 대부분 동의한다. 내가 염두에두고있는 시나리오는 실제로 최상위 오류 처리기입니다 (로그 충돌 만). 그러나 전체 충돌 정보가 어떻게 든 도착해야합니다. UnobservedTaskException과 Application Handler는 오류가 관찰되었지만 삼켜지기 때문에 대기 상태에서이를 보장하지 않습니다.; 나는 오류가 삼킨 것을 볼 때 매우 긴장합니다. 한 달 동안 생산이 심각한 오류로 고통을 겪었고 아무도 눈치 채지 못하는 경우가 너무 많았습니다. - usr
  • UnobservedTaskException여전히 키우고있다.관찰되지 않은 예외가 발생하면 (프로세스를 더 이상 크래시하지 않습니다.) 관찰 된, 삼켜지는 예외가있는 유일한 경우는WhenAll, 그리고 여러 예외가있을 때만 발생합니다. 그 중 하나는아니삼켰다. 너 진짜해야 해.시험예외를 무시하는 방법await. - Stephen Cleary
  • @BatteryBackupUnit : 작업에는 두 가지 주요 용도가 있습니다. 하나는 "미래" 비동기 코드의 경우, 다른 하나는 "작업 단위" 병렬 코드 용. 귀하의 접근 방식은 변경 / 고정이 필요하다고 생각합니다.AggregateException적어도. 언제든지토론을위한 아이디어 게시. 옆으로 : 기술적으로,await해당 항목을 푸십시오.AggregateException하지만 괜찮은 정신 모델입니다. - Stephen Cleary

3

나는 늦었지만 당신이 원하는 것을하는이 깔끔한 작은 트릭을 발견했다. 대기중인 작업에 대해 전체 예외 집합을 사용할 수 있으므로이 작업의 대기 또는 .Result를 호출하면 집계 예외가 발생합니다.

    static void Main(string[] args)
    {
        var task = Run();
        task.Wait();
    }
    public static async Task Run()
    {

        Task[] tasks = new[] { CreateTask("ex1"), CreateTask("ex2") };
        var compositeTask = Task.WhenAll(tasks);
        try
        {
            await compositeTask.ContinueWith((antecedant) => { }, TaskContinuationOptions.ExecuteSynchronously);
            compositeTask.Wait();
        }
        catch (AggregateException aex)
        {
            foreach (var ex in aex.InnerExceptions)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }

    static Task CreateTask(string message)
    {
        return Task.Factory.StartNew(() => { throw new Exception(message); });
    }


3

예외 처리 (작업 병렬 라이브러리)

더 말할 수는 있지만 패딩 만있을뿐입니다. 그걸 가지고 놀아, 그들이 말하는대로 작동합니다. 조심해야합니다.

너는 이것을 원할지도 모른다.

하나님 (Jon Skeet)이 예외 처리를 기다리고 있다고 설명합니다.

(개인적으로 나는 기다리는 것을 멀리 부끄러워하지만, 그건 내가 선호하는 것임)

댓글에 대한 응답으로 (댓글 응답이 너무 깁니다)

그런 다음 스레드를 모범 사례와 유사한 인수에 대한 출발점으로 사용하십시오. 여기에 소스가 있습니다.

예외를 전달하기 위해 코드를 구현하지 않으면 (예 : 기다리고있는 비동기 패턴이 아마도 랩핑됩니다 ... 이벤트를 발생시킬 때 이벤트 args 객체에 추가합니다.) 예외는 행복하게 삼킨다. 임의의 수의 스레드를 실행하고 실행하는 시나리오가 있으면 순서 나 스레드를 종료하는 시점을 제어 할 수 없습니다. 또한 하나의 오류가 다른 오류와 관련이 있으면이 패턴을 사용하지 않아야합니다. 그러므로 당신은 나머지 부분의 실행이 completly independent임을 강력히 암시합니다. IE강하게이러한 스레드에 대한 예외가 이미 예외로 처리되었음을 의미합니다. 발생한 스레드 (예외)에서 예외를 처리하는 것 이상의 작업을 수행하려면 참조로 전달되는 잠금 모음에 예외를 추가해야합니다. 예외는 예외로 간주하지 않고 조각으로 간주합니다 정보의 - 동시 가방을 사용하여 그것이 출현 한 컨텍스트를 식별하는 데 필요한 정보에서 예외를 래핑하십시오.

사용 사례를 모으지 마십시오.


  • 첫 번째 링크는 다루지 않습니다.await. 두 번째 것은 유효한 대답을 포함하고 있습니다. 감사. 다른 답변을 며칠 동안이나 들여 보내 드리겠습니다.베스트해결책. - usr
  • 것은 당신의 유스 케이스에 동의하지 않는다. 나는 당신이 사용하고있을 때 당신이 각 태스크가 그것의 자신의 예외를 처리한다고 가정하고 있다고 생각한다 - 기다리는 것은 당신이 다른 태스크를 취소 할 수없는 오퍼레이션에서 무언가가 깨 졌음을 말해 준다. 아니면 적어도 그것이 그 패턴으로 생각됩니다. 작업 외부에서 예외를 처리하려는 경우 맨 위 패턴을 사용해야합니다. 나는 그것을 설명해야 할 것입니다. 또한 집계 예외 - 질문 -을 원한다면 첫 번째가 선택이어야합니다. - John Nicholas
  • 맨 위 패턴은 주소를 기다리는 것보다 완전히 다른 유스 케이스 인 블로킹을 포함합니다. 유효한 유즈 케이스이지만 광산이 아닙니다. 여기에 비동기 IO 및 비 차단이 필요합니다.; 또한 작업이 반드시 자체 오류를 처리하지는 않습니다. 이 경우 오류 전파가 필요하지 않습니다. 오류는가정의제어의 정상 흐름이 중단되도록 전파하십시오. 내가 원하는 건모든오류뿐만 아니라 임의의 오류. - usr
  • updated ... ofc 태스크는 자체 오류를 처리합니다 ... 작업 단위는 작업 단위 주위의 캡슐화 단위입니다. 그 예외를 처리하는 작업은 내가 말하고자하는 가장 확실한 부분이다. 그렇지 않으면 100 개의 스레드가 모두 예외를 던져서 코드의 한 부분을 블로킹하여 스스로를위한 악몽을 자아 내고있다. 어쨌든 그것은 태스크 자체에 기본적으로 앉아 있다는 것을 깨닫게 될 것입니다 ... 아니면 저와 절충하고 예외를 처리하기 위해 람다를 전달하십시오. - John Nicholas
  • 신의 기사에 대한 @ Johnnicholas 참조가 깨졌습니다 ( - Konstantin Chernov

0

나는 예상했던 예외만을 잡으려고 연습을 포기하고 싶지 않습니다. 이것은 다음 확장 메소드로 연결됩니다.

public static async Task NoSwallow<TException>(this Task task) where TException : Exception {
    try {
        await task;
    } catch (TException) {
        var unexpectedEx = task.Exception
                               .Flatten()
                               .InnerExceptions
                               .FirstOrDefault(ex => !(ex is TException));
        if (unexpectedEx != null) {
            throw new NotImplementedException(null, unexpectedEx);
        } else {
            throw task.Exception;
        }
    }
}

소비 코드는 다음과 같이 갈 수 있습니다.

try {
    await Task.WhenAll(tasks).NoSwallow<MyException>();
catch (AggregateException ex) {
    HandleExceptions(ex);
}

뼈대 예외는 동시 세계에서와 같은 효과를냅니다.MyException우연히. 포장은NotIplementedException원본 스택 추적을 풀지 않습니다.

연결된 질문


관련된 질문

최근 질문