2017-04-05 9 views
3

をキャンセルされました。ここでの目標は、taskが100ms以内に完了するかどうかを確認することです。もしそうでなければ、いくつかのロギングを処理したい(この特定のタスクにはリソースリークがあるので、タイムアウトでラップすることで回避しようとしています)。これはここのガイダンスから引き出されています。Debugging Task.WhenAny and Push NotificationsTask.WhenAnyタスクは、次のコードでは

なぜこのようなことが起こりますか、この例外がスローされないようにするにはどうすればよいですか?

答えて

4

これは、taskが時間内に(100ms以内に)完了できなかったが、後で完了できたときに発生します。あなたはTaskContinuationOptions.OnlyOnFaultedであなたの継続を実行し、元のタスクが失敗しなかった場合、そのようなタスクは取り消されました。あなたawaitの結果がContinueWithなので、あなたのタスクにフォルトがない場合、あなたの継続はキャンセルされ、例外があります。

一般にタイムアウトを処理するこのような方法はあまり意味がありません。タイムアウトに達しても、元のタスクが完了するまで待機して例外を記録するためです。私はあなたの継続の前にawaitを取り除かなければならないと思います。その後、タイムアウト時にコードは続行されますが、タスクが後で失敗すると記録されます。

あなたのコードには別の問題があります。Task.WhenAnyは決して投げません。したがって、この条件:taskに障害が発生する可能性があるため

await Task.WhenAny(task, Task.Delay(100)) == 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を実行します。それが間違っていなければ、継続はキャンセルされます。

この特定の例外の処理に関係しない2つの選択肢は、何も待たず(タスクが実行を終了したかどうかわからない)、または元のタスクを待つことです。待っていると、タスクの継続はその時点で実行されるか、取り消されます。

キャンセルを処理しても問題ない場合は、必要に応じてタスク継続の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は無視されますが、待たれていないタスクについて警告が表示されます。警告を無視するか、特定のTaskの設定の一部としてcontinuationを見ることができます。それによって我々は適切なオプションでタスクを作成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

関連する問題