2014-03-28 1 views
3

어떤 접근 방법이 일반적으로 메모리 및 리소스 사용면에서 더 효율적인지 궁금합니다.작업을 사용하여 비동기 기능 다시 시도 - 어떤 접근 방식이 더 효율적입니까?

접근 방식 # 1을 사용하면 작업 객체가 어떻게 생성되고 스레드가 회전되는지 시각화하는 데 어려움을 겪고 있습니까? 누군가 제발 제쳐두고 세부적으로 덮개 밑에서 어떻게되는지 설명해 주시겠습니까?

둘 사이에 차이가 없다면 # 1을 사용하고 싶습니다. (비동기가 발생하지 않도록하고 싶습니다.) # 2를 사용하면 컴파일러가 상태 머신을 생성하고 리턴 할 것이라는 것을 이해합니다. OTOH, # 1은 재귀 적으로 개념적으로 보이지만 다른 스택에서 대기하는 한 스택 프레임처럼 전통적인 의미에서 재귀적일 것인가?

접근법 # 1 :

internal static Task ExecuteAsyncWithRetry(Func<Task> methodToExecute, Func<bool> shouldRetry) 
    { 
     var tcs = new TaskCompletionSource<object>(); 

     try 
     { 
      return methodToExecute().ContinueWith<Task>((t) => 
      { 
       if (t.IsFaulted || t.IsCanceled) 
       { 
        if (shouldRetry()) 
        { 
         return ExecuteAsyncWithRetry(methodToExecute, shouldRetry); 
        } 
        else 
        { 
         tcs.SetException(t.Exception); 
        } 
       } 
       else 
       { 
        tcs.SetResult(null); 

       } 

       return tcs.Task; 
      }, TaskContinuationOptions.ExecuteSynchronously).Unwrap(); 
     } 
     catch(Exception ex) 
     { 
      tcs.SetException(ex); 
     } 

     return tcs.Task; 
    } 

접근법 # 2 (둘 사이 예외 전파의 차이를 무시하는) 다른 예외 취소 전파 게다가

internal static async Task ExecuteWithRetry(Func<Task> methodToExecute, Func<bool> shouldRetry) 
    { 
     while (true) 
     { 
      try 
      { 
       await methodToExecute(); 
      } 
      catch(Exception ex) 
      { 
       if(!shouldRetry()) 
       { 
        throw; 
       } 
      } 
     } 
    } 
+0

두 번째 방법은 읽고 이해하는 것이 더 명확합니다. 내 경험에 비추어 볼 때 가능한 한 비동기 적으로 기다려야합니다. 그리고 당신의 경우에 가능합니다. 하지만 두 번째 경우에 어떤 종류의 종료 조건이 누락되었습니다. 모든 것이 잘되면'methodToExecute()' 이 반복해서 실행됩니다. – Krumelur

+0

AggregateException을 처리하지 않기 때문에 오류 정보가 손실됩니다. 이 같은 일반적인 도우미 메서드에 바람직하지 않습니다. 예외를 해석하는 일은 아닙니다. – usr

+0

감사합니다. 그러나, 오류 정보 부분을 잃어 버리셨습니까? UnWrap()과 같은 동작을 기다리지 말고 내부 작업의 오류/예외를 전파해야합니까? – Kakira

답변

3

는 서로 큰 차이가있다.

TaskContinuationOptions.ExecuteSynchronously 때문에 첫 번째 경우에 작업이 완료된 동일한 스레드에서 계속 실행됩니다.

두 번째 경우에는 동기화 컨텍스트가있는 스레드에서 methodToExecute이 호출 된 경우 원래 동기화 컨텍스트에서 실행됩니다.

첫 번째 접근 방식이 더 효율적 일지라도 이해하기가 어려울 수 있습니다 (특히 1 년 후에 내게 또는 다른 사람이 돌아 오는 경우).

나는 KISS principle를 따라 하나의 개정으로, 두 번째로 다루고 싶어요 :

는 "OTOH, # 1 재귀 개념적으로 보인다 코멘트를 해결하기 위해 업데이트

await methodToExecute().ConfigureAwait(false); 

그러나 하나의 스택 프레임에서 다른 것을 기다리는 것처럼 전통적인 의미에서 에 재귀 적으로 나타날 것인가? " 그것은 다른 스택 프레임에 비동기 적으로 동일한 스택 프레임에 반복적으로 발생, 또는 여부 # 1의 경우

완전히 내부 methodToExecute 무슨 일이 일어나고 있는지에 따라 달라집니다. methodToExecute 내부에서 자연스럽게 비동기 API를 사용하는 경우 대부분의 경우 기존 재귀가 발생하지 않습니다. 예 : HttpClient.GetStringAsynccompletes on a random IOCP pool threadTask.Delay은 임의 작업자 풀 스레드에서 완료됩니다.

그러나 비동기 API조차도 동일한 스레드 (예 : MemoryStream.ReadAsync 또는)에서 동 기적으로 완료 될 수 있습니다.이 경우 재귀가 발생합니다.

또는 methodToExecute 안에 TaskCompletionSource.SetResult을 사용하면 동기 계속을 트리거 할 수 있습니다.

재귀 가능성을 피하려면 Task.Run (또는 Task.Factory.StartNew/Task.Unwrap)을 통해 methodToExecute으로 전화하십시오. 또는 더 나은 방법은 TaskContinuationOptions.ExecuteSynchronously을 삭제하는 것입니다.

# 2의 경우 초기 스레드에 동기화 컨텍스트가있는 경우에도 동일한 시나리오가 가능합니다.

+0

물론, 답변을 주셔서 감사합니다. 나는 그들의 차이점에 대해 알고있었습니다. 그러나 나는 개인적인 이해/개념의 고착에 대한 이러한 뉘앙스를 넘어선 대답을 기대하고 있었다. 특히 # 1을 사용하면 무엇이 진행되고 있는지 알고 싶습니다. – Kakira

+0

@Kakira, 이제 제가 따라합니다. 이 코드를 직접 작성했다면 어떻게 작동하는지 이해해야합니다. 그렇지 않다면 어떤 부분을 이해하지 못합니까? FYI, [관련 질문] (http://stackoverflow.com/q/21345673/1768303). – Noseratio

+0

내가 말했듯이 (이것이 내 코드이기 때문에 걱정하지 않아도됩니다.) 나는 커버 아래에서 진행되는 것을 시각화하려고합니다. 나는 구체적으로이 부분을 이해하려고 노력하고있다 : "OTOH, # 1은 재귀 적으로 개념적으로 보이지만 다른 스택을 기다리는 것과 같은 전통적인 의미에서 재귀적일 것인가?" – Kakira

관련 문제