2011-12-30 3 views
23

몇 가지 작업을 생성 한 다음 반환하기 전에 WaitAll을 사용하여 작업을 기다리는 방법이 있습니다. 문제는 작업이 취소 된 경우 WaitAll이 TaskCanceledException 초를 포함하는 AggregateException을 던졌습니다. 진짜 오류를 나타냅니다TaskCanceledExceptions을 throw하지 않고 작업을 대기하려면 어떻게해야합니까?

  • 예외 : WaitAll 두 개의 서로 다른 상황에서 예외를 던질 것을 의미

    . 이것들은 우리가 다루는 방법을 알지 못하는 상태가 있음을 의미합니다. 처리가 끝날 때까지 처리되지 않은 예외로 전파해야합니다.

  • 사용자가 취소 버튼을 클릭했음을 나타내는 예외입니다. 즉, 작업이 취소되고 정리 된 후 프로그램이 계속 정상적으로 실행되어야합니다. 그것은 완전히 비 예외적 인 상황에 던져 예외, 그래서 정상적인 제어 흐름을 재개하기 위해 그것을 잡을 필요가 :

는 후자는 정면으로 vexing exception의 정의에 맞는. 다행히도 쉽게 잡을 수 있습니다. 맞습니까? catch (AggregateException)을 추가하기 만하면됩니다. 치명적인 오류가 발생하면 던져지는 것과 동일한 유형입니다.

돌아 오기 전에 작업이 끝나기를 기다려야합니다 (더 이상 데이터베이스 연결, 파일 핸들 또는 다른 것을 사용하고 있지 않음을 알아야합니다). 그래서 WaitAll 또는 다른 것이 필요합니다. 비슷한. 그리고 오류가 발생한 작업이 있으면 해당 예외가 처리되지 않은 예외로 전파되기를 원합니다. 나는 단지 취소를 원하지 않는다.

취소 된 작업에 대해 WaitAll에서 예외가 발생하지 않도록하려면 어떻게해야합니까?

+0

사용하십시오 CancellationToken 걸리는 과부하를 간다. –

+0

@HansPassant, 그 과부하에 대한 문서는 실제로 그것이 토큰을 사용하는 것을 말하지 않습니다. 해당 토큰과 관련된 TaskCanceledExceptions를 무시합니까? 아니면 단순히 토큰이 취소 된 경우 일찍 반환됩니까? 모든 작업이 중단 될 때까지 돌아 가지 않아야합니다. –

+1

CancellationToken을 사용하여 작업을 취소합니다. 그리고 이제는 특정 OperationCancelException을 필터링하십시오. –

답변

28

AggregateException은 이러한 상황에 사용할 수있는 Handle 방법을 제공합니다. 예를 들어 당신이 TaskCanceledException을 무시하려는 경우 당신은 할 수 있습니다 :

var all = new AggregateException(
    new NullReferenceException(), 
    new TaskCanceledException(), 
    new TaskCanceledException(), 
    new InvalidOperationException(), 
    new TaskCanceledException()); 

try 
{ 
    throw all; 
} 
catch (AggregateException errors) 
{ 
    errors.Handle(e => e is TaskCanceledException); 
} 

모든 예외 유형 TaskCanceledException의 경우, Handle 방법은 어떤 예외를 throw하지 않습니다; 그렇지 않으면 처리되지 않은 예외 만 포함하는 새 AggregateException이 발생합니다.

1

João Angelo's suggestion을 바탕으로, 여기에 작업 클래스 확장

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading; 
using System.Threading.Tasks; 

namespace MySharedLibrary.Extensions 
{ 
    public static class TaskExtensions 
    { 

     // This code is based João Angelo's stackoverflow suggestion https://stackoverflow.com/a/8681687/378115 

     // Use this when a CancellationTokenSource is used 
     public static void SafeWait(this Task TargetTask, CancellationTokenSource TargetTaskCancellationTokenSource) 
     { 
      if (TargetTaskCancellationTokenSource.IsCancellationRequested == false) 
      { 
       TargetTaskCancellationTokenSource.Cancel(); 
      } 
      SafeWait(TargetTask); 
     } 

     // Use this when no CancellationTokenSource is used 
     public static void SafeWait(this Task TargetTask) 
     { 
      try 
      { 
       if (TargetTask.IsCanceled == false) 
       { 
        TargetTask.Wait(); 
       } 
      } 
      catch (AggregateException errors) 
      { 
       errors.Handle(e => e is TaskCanceledException); 
      } 
     } 

    } 
}