2017-04-05 5 views
3

취소했다 : 나는 가끔 await task.ContinueWith에 대한 호출에 A task was cancelled을 얻을Task.WhenAny은 - 작업은 다음 코드에서

 if (await Task.WhenAny(task, Task.Delay(100)) == task) { 
      success = true; 
     } 
     else { 
      details += "Timed out on SendGrid."; 
      await task.ContinueWith(s => 
      { 
       LogError(s.Exception); 
      }, TaskContinuationOptions.OnlyOnFaulted); 
     } 

. 내 목표는 여기서 task 100ms 이내에 완료되는지 확인하는 것입니다 - 그렇지 않으면 일부 로깅을 처리하려면 (이 특정 작업은 리소스 누수가 있으므로 시간 제한에 배치하여 해결하려면 노력하고있어). 여기에있는 지침에서 가져온 것입니다 : Debugging Task.WhenAny and Push Notifications

왜 이런 일이 벌어지고 있으며,이 예외가 발생하지 않도록하려면 어떻게해야합니까?

답변

4

이것은 task이 (100ms 단위로) 완료 할 수 없었지만 나중에 완료 할 수 있었을 때 발생합니다. TaskContinuationOptions.OnlyOnFaulted으로 계속 작업을 수행하면 원래 작업에 오류가 발생하지 않은 경우 이러한 작업이 취소됩니다. 당신 awaitContinueWith의 결과 그래서 만약 당신의 작업이 잘못되지 않았다면 - 당신의 계속은 취소되고 당신은 예외가 있습니다.

일반적으로 타임 아웃을 처리하는 이러한 방법은 많은 의미가 없습니다. 타임 아웃 후에도 원래 작업이 완료 될 때까지 기다렸다가 예외를 기록하기 때문입니다. 계속하기 전에 await을 삭제해야한다고 생각합니다. 그런 다음 코드는 제한 시간에 계속되지만 작업이 나중에 실패하면 기록됩니다.

코드에 다른 문제가 있습니다. Task.WhenAny은 절대로 던지지 않습니다. 따라서이 조건 :

await Task.WhenAny(task, Task.Delay(100)) == task 

task이 잘못되었을 수 있으므로 성공하지 못했습니다. WhenAny이 작업 완료를 나타내더라도 항상 task.Statustask.Exception을 확인하십시오. 그건 그렇고, 귀하의 질문에 링크 된 대답은 이것을 언급합니다.

업데이트 : 당신은 VS가 기다리고 있지 통화에 대한 경고를 좋아하지 않는 경우 -이 특정 라인을 비활성화하거나이 같은 확장 방법을 사용할 수 있습니다 : 다음

static class TaskExtensions 
{ 
    public static void Forget(this Task task) 
    { 
     // do nothing 
    } 
} 

그리고 :

task.ContinueWith(s => { 
    Logger.Write(s.Exception); 
}, TaskContinuationOptions.OnlyOnFaulted).Forget(); 

이 과정을 수행해도 아무런 해가 없습니다. VS는 기다려지지 않을 가능성이있는 모든 호출에서이 경고를 발행합니다.

+0

내가 완전히 명확하지 않다. 누출 된 API를 처리하려고 할 때 예외를 던지지 않고 처리하는 것이 어떻습니까? – SB2055

+0

대답이 업데이트되었습니다. 작업 전에 'await'을 제거하십시오 .ContinueWith. – Evk

+0

그렇다면 VS는 비동기 호출을 기다리지 않는다고 불평합니다. – SB2055

0

작업 계속을 실행하지 않으면 취소됩니다. 작업 계속은 OnlyOnFaulted를 실행합니다. 그것이 잘못되지 않으면, 그 연속은 취소 될 것입니다.

해당 예외 처리와 관련이없는 두 가지 선택 사항은 아무 것도 기다리지 않고 (작업이 실행 완료 여부를 알 수 없음) 또는 원래 작업을 기다리는 것입니다. 대기가 완료되면 작업 계속이 실행되거나 취소됩니다.

취소 작업을 처리하는 것이 좋으면 필요에 따라 Task Continuation에서 TaskCancelledException (또는 적절하게 AggregateException)을 잡아 예외를 버리십시오.

내 생각에이 특별한 경우 (예외 로깅)는 원래 작업을 다시 기다리고 TaskCancelledException을 처리하고, 작업 계속이 필요하지 않으며 완전히 비동기/준수를 기다리는 것입니다.

0

이미 @Evk에서 언급했듯이 취소 된 것은 Continuation입니다. 그러나 그 연속성은 구성의 조각으로 간주 될 수 있으며 따라서 Task이 생성 될 때 설정되어야한다고 덧붙이고 싶습니다. 당신의 예외가 여전히 기록됩니다 이러한 접근 방식에서

var task = Task.Delay(500).ContinueWith(s => 
{ 
    LogError(s.Exception); 
}, TaskContinuationOptions.OnlyOnFaulted); 

if (await Task.WhenAny(task, Task.Delay(100)) == task) { 
    success = true; 
} else { 
    details += "Timed out on SendGrid."; 
} 

과 논리 만 Task 시간이 초과 것을 알고에 수행한다 : 다음과 같은 고려하십시오. 로직의 나머지 부분에서 시간 제한 외에 Exception에 대한 지식이 필요한 경우 이미 설명한대로 awaitTask이 다시 필요합니다. task가 정의된다

원본 코드 (1)이 단계에서 우리가 볼 수없는 명확성을 위해

업데이트.

//task is undeclared in this snippet 
if (await Task.WhenAny(task, Task.Delay(100)) == task) { 
    success = true; 
} 
else { 
    details += "Timed out on SendGrid."; 
    await task.ContinueWith(s => 
    { 
     LogError(s.Exception); 
    }, TaskContinuationOptions.OnlyOnFaulted); 
} 

(2)는 그냥 예를 들어 모의 작업을 추가하자

var task = Task.Delay(500); //defines task as a Task that will complete in 500ms 
if (await Task.WhenAny(task, Task.Delay(100)) == task) { 
    success = true; 
} 
else { 
    details += "Timed out on SendGrid."; 
    await task.ContinueWith(s => 
    { 
     LogError(s.Exception); 
    }, TaskContinuationOptions.OnlyOnFaulted); 
} 

(3) 다음으로, 우리는 await task.ContinueWith 우리가 TaskCancelledException가 발생 할 수 있습니다. await이 제거되면 Exception은 무시할 수 있지만 기다리지 않은 작업에 대한 경고가 표시됩니다. 우리는 경고를 무시하거나 continuation이 특정 Task의 구성의 일부로 볼 수 있음을 알 수 있습니다. 그것으로 우리는 우리가 적절한 옵션과 함께 작업을 만들 Continuation, 즉 TaskContinuationOptions.OnlyOnFaulted을 구성 할 수 있습니다

//Add continuation configuration where task is created 
var task = Task.Delay(500).ContinueWith(s => 
{ 
    LogError(s.Exception); 
}, TaskContinuationOptions.OnlyOnFaulted); 

if (await Task.WhenAny(task, Task.Delay(100)) == task) { 
    success = true; 
} else { 
    details += "Timed out on SendGrid."; 
    //removed continuation from here. 
} 
+0

혼란 스럽네요 - 원래 '과제'는 여기에 있습니까? – SB2055

+0

@ SB2055 태스크의 이름이'task'인데 태스크가 설정되었을 때 태스크에'continuation'을 설정할 수 있고 타임 아웃 후에 태스크를 설정할 수 있음을 보여주기 위해'Task.Delay'를 지정했습니다. 'Task.Delay (500)'를 당신의 실제 작업을 만드는 것과 바꾸실 수 있습니다. – JSteward

+0

내가 제공 한 코드를 활용하면이 대답이 더 좋을 것이라고 생각합니다. 나는 여기서 당신의 의도를 이해하지 못합니다. – SB2055