2012-01-10 2 views
16

왜 작업에서 발생한 예외가 침묵을 제외하고 특정 예외가 작업 및 예외 침묵

try 
{ 

Task task = new Task(
() => { 
      throw null; 
     } 
     ); 
    task.Start(); 
} 
catch 
{ 
    Console.WriteLine("Exception"); 
} 

완전한 침묵에 성공적으로 프로그램 실행

을 던져되어 있는지 알 수 없다! 스레드의 동작이

try 
{ 

Thread thread = new Thread(
() => { 
      throw null; 
     } 
     ); 
    thread .Start(); 
} 
catch 
{ 
    Console.WriteLine("Exception"); 
} 

널 포인터 예외 다른 이 경우에 발생합니다. 차이점은 무엇입니까?

답변

15

해당 시나리오의 동작은 가지고있는 프레임 워크에 따라 다릅니다. 4.0에서는 실제로주의해야합니다. TaskScheduler.UnobservedTaskException을 처리하지 않으면 나중에이 수집/확정되면 오류가 발생하고 은 프로세스을 삭제합니다.

TaskScheduler.UnobservedTaskException += (sender, args) => 
{ 
    Trace.WriteLine(args.Exception.Message); // somebody forgot to check! 
    args.SetObserved(); 
}; 

4.5, IIRC에서 변경되었습니다.

는이 실패 할 수 있습니다, 당신은 등록 할 수 Task 것을 계속의 결과를 확인하려면 - 즉 ContinueWith를 호출하고 결과의 예외를 확인하십시오. 또는 .Result의 작업 (암시 적으로 을 수행하는)에 액세스하면 발생한 예외가 다시 처리됩니다. 태스크의 결과를 관찰하면 완료 플래그가 지워 지므로 태스크를보다 저렴하게 수집 할 수 있습니다.

+0

버전 번호를 5.0에서 4.5로 변경했습니다. 괜찮습니다. (그것은 C# 5이지만 .NET 4.5입니다.) –

+0

@Jon 전혀 아님; 정말로 나를 고쳐 주셔서 감사합니다. –

5

아니요, 작업은 스레드가 아닙니다. 작업은 높은 수준의 추상화를 나타내며 본질적으로 병렬 처리가 가능한 작업 단위입니다. 스레드는 작업 단위를 실행합니다.

첫 번째 예에서는 작업 단위를 작성한 다음 자체 작업을 실행하도록 지시합니다 (태스크의 구현 세부 사항이 어떻게 수행되는지). 두 번째 예에서는 작업 단위를 명시 적으로 예약하는 반면 (Task 구현과는 다른 방식으로 나타납니다).

+0

그래도 예외 처리는 어떻게됩니까? –

+0

OP는'Thread'가 던지는 반면'OP'가 던지지 않는 이유에 더 관심이 있습니다. – Oded

+2

@sleimanjneidi 작업은 작업을 질의 할 때 예외를 저장합니다. 스레드에는 고유 한 예외 처리가 없으며 예외가 포착 될 때까지 스택까지 전파됩니다. –

1

메인 스레드에서 예외를 기다리지 않으므로 Task의 예외가 발생하지 않는다고 생각합니다. 당신은 계속하고 있습니다. task.Wait()을 입력하면 메인 스레드에서 예외가 발생합니다.

+1

매우 사실이지만, 공정해야합니다. 대부분의 경우에 우리는 즉시 '대기'를 호출하지 않을 것으로 예상합니다. –

4

다음은 .NET 4.0 및 기본 TaskScheduler 사용을 가정합니다. 모든 통지

첫째는 예외은 통과 내부 대표를 제기하는 것을, 그래서 그들은하지 당신이 (논리적으로) 당신의 catch하고있는 일에 다른 스레드에서 발생합니다. 그래서 어떻게 든 예외는 스레드/작업을 시작한 델리게이트/람다 코드를 실행하는 스레드에서 전파되어야합니다.

Task에 대해 라이브러리는 자신의 스레드가 아닌 스레드를 호출하도록 선택할 수 있다고 생각합니다. (단, Parallel.ForEach 등의 경우에만 해당됩니다. "알몸"이 아닌 Task 개체).

두 개의 일이 들어

은 두 가지가 충족되어야합니다

  1. 호출 스레드가 여전히 활성 상태입니다. 그렇지 않으면 실제로 캐치를 수행 할 수있는 것이 아무것도 없습니다.
  2. 스레드/작업에서 발생한 예외는 어떻게 든 유지되어야하며 예외를 잡을 수는 없습니다.

그렇다면 두 예제 모두 스레드/작업 완료를 기다리지 않아도됩니다. 첫 번째 예에서는 task.Wait() (또는 이와 유사한 것)이 누락되었으며 두 번째 예에서는 thread.Join()이 누락되었습니다. 테스트 코드 타이밍 동작에 따라 스레드/태스크 (위의 항목 1)에서 예외를 관찰 할 수 없을 수도 있습니다.

  • 작업 예 : 당신은 두 통화를 추가

    경우에도이 나 (다시 .NET 4.0)에 대한 일어나는 것이다 task.Wait()에 대한 호출이 실제로 원래 작업의에서 처리되지 않은 왼쪽 예외를 재발생 델리게이트 (이것은 TPL이 당신을 위해 내부적으로 수행하는 것입니다), System.AggregateException 안에 포장합니다. 좀 더 정밀한 것을 사용하고 평평한 "catch-all"을 사용하면 볼 수 있습니다.

  • 스레드 예 : 대리인에 의해 제기 예외가 처리되지 않은 남아 즉

(당신이 unhandled exceptions differently 다루는 아무것도하지 않는 한) 다음과 같이 응용 프로그램이 종료 나는 예제를 할 것이다 :

// Thread example 
var thread = new Thread(() => { throw null; }); 
thread.Start(); 
thread.Join(); 
// Should never reach here, depending on timing sometimes not even 
// until the Join() call. The process terminates as soon as the other 
// thread runs the "throw null" code - which can logically happen somewhere 
// after the "start" of the "Start()" call. 

// Task example 
try 
{ 

    var task = new Task(() => { throw null; }); 
    task.Start(); 
    task.Wait(); 
} 
catch (AggregateException ex) 
{ 
    Console.WriteLine("Exception: " + ex); 
}