0

エラーが待ちタイムアウト後Task.Waitで二回投げされている。この問題は、私はTask.Waitを使用していたという事実から生じているようだC#/タスクエラーが待ちタイムアウト後Task.Waitで2回記録されている

タイムアウト。問題は、タスクタイムアウトが2回記録された後にスローされた例外か、タイムアウトが記録されないうちにスローされたエラーのいずれかです。私はコードを追加し、シナリオをよりよく理解するために使ったテストを行いました。

このテストの背後にあるアイデアは、例外がスローされる(3秒で)前に、(2秒で)タイムアウトが強制されるということです。このような状況で例外はどうなりますか?以下はその結果です。 「ブーム」例外は決して報告されない。これは、タスクでは観察されない例外として残っています。

  [MassUpdateEngine.cs] 
     // Package the collection of statements that need to be run as a Task. 
     // The Task can then be given a cancellation token and a timeout. 
     Task task = Task.Run(async() => 
     { 
      try 
      { 
       Thread.Sleep(3000); 
       throw new Exception("boom"); 

       // Checking whether the task was cancelled at each step in the task gives us finer grained control over when we should bail out. 
       token.ThrowIfCancellationRequested(); 
       Guid id = SubmitPreview(); 
       results.JobId = id; 

       token.ThrowIfCancellationRequested(); 
       bool previewStatus = await GetPreviewStatus(id, token); 
       Logger.Log("Preview status: " + previewStatus); 

       token.ThrowIfCancellationRequested(); 
       ExecuteUpdate(id); 

       token.ThrowIfCancellationRequested(); 
       bool updateStatus = await GetUpdateStatus(id, token); 
       Logger.Log("Update status: " + updateStatus); 

       token.ThrowIfCancellationRequested(); 
       string value = GetUpdateResults(id); 
       results.NewValue = value; 
      } 
      // It appears that awaited methods will throw exceptions on when cancelled. 
      catch (OperationCanceledException) 
      { 
       Logger.Log("***An operation was cancelled.***"); 
      } 
     }, token); 

     task.ContinueWith(antecedent => 
     { 
      //Logger.Log(antecedent.Exception.ToString()); 
      throw new CustomException(); 
     }, TaskContinuationOptions.OnlyOnFaulted); 



     [Program.cs] 
     try 
     { 
      MassUpdateEngine engine = new MassUpdateEngine(); 

      // This call simulates calling the MassUpdate.Execute method that will handle preview + update all in one "synchronous" call. 
      //Results results = engine.Execute(); 

      // This call simulates calling the MassUpdate.Execute method that will handle preview + update all in one "synchronous" call along with a timeout value. 
      // Note: PreviewProcessor and UpdateProcessor both sleep for 3 seconds each. The timeout needs to be > 6 seconds for the call to complete successfully. 
      int timeout = 2000; 
      Results results = engine.Execute(timeout); 

      Logger.Log("Results: " + results.NewValue); 
     } 
     catch (TimeoutException ex) 
     { 
      Logger.Log("***Timeout occurred.***"); 
     } 
     catch (AggregateException ex) 
     { 
      Logger.Log("***Aggregate exception occurred.***\n" + ex.ToString()); 
     } 
     catch (CustomException ex) 
     { 
      Logger.Log("A custom exception was caught and handled.\n" + ex.ToString()); 
     } 

例外が発生していないため、適切に記録されていないため、これは機能しません。

この情報は、次のルールにつながる:

  1. が.ContinueWithから投げないでください。ここからスローされた例外は、呼び出しスレッドにマーシャリングされません。これらの例外は、観察されない例外として残され、効果的に食べられる。
  2. タスクからの例外は、タイムアウトで待機を使用しているときに、呼び出し元のスレッドにマーシャリングされる場合もあれば、マーシャリングされない場合もあります。 Wait呼び出しのタイムアウトの前に例外が発生した場合、例外は呼び出しスレッドにマーシャリングされます。 Waitコールのタイムアウト後に例外が発生した場合、例外はタスクの監視されていない例外のままです。

ルール2はかなり醜いです。このシナリオでは、例外を確実にログに記録するにはどうすればよいですか? .ContinueWith/OnlyOnFaultedを使用して例外を記録することができます(下記参照)。

 task.ContinueWith(antecedent => 
     { 
      Logger.Log(antecedent.Exception.ToString()); 
      //throw new CustomException(); 
     }, TaskContinuationOptions.OnlyOnFaulted); 

例外が待機コールのタイムアウトの前に発生する場合は、その例外は呼び出し元のスレッドにマーシャリングされ、グローバルな未処理の例外ハンドラによって処理(及びログイン)し、その後に渡されますれます.ContinueWithタスク(およびログに記録される)は、同じ例外に対して2つのエラーログエントリを生成します。

私はここで欠けているものがなければなりません。どんな助けもありがとう。

答えて

0

.ContinueWithから投げないでください。ここからスローされた例外は、呼び出しスレッドにマーシャリングされません。これらの例外は、観察されない例外として残され、効果的に食べられる。タスクが(ではない、それはContinueWithを使用して作成されたため)が観察されていないため、

観測されない例外の原因です。 ContinueWithから返されたタスクは無視されます。

より適切なアドバイスはnot use ContinueWith at allです(詳しくは私のブログをご覧ください)。あなたがawaitを使用するように変更すると、答えが明確になる:

public static async Task LogExceptions(Func<Task> func) 
{ 
    try 
    { 
    await func(); 
    } 
    catch (Exception ex) 
    { 
    Logger.Log(ex.ToString()); 
    } 
} 

public static async Task<T> LogExceptions<T>(Func<Task<T>> func) 
{ 
    try 
    { 
    return await func(); 
    } 
    catch (Exception ex) 
    { 
    Logger.Log(ex.ToString()); 
    } 
} 

async/awaitパターンがより明確に本当にここで何が起こっているかを示しています。コードは代替として使用されなければならないwrapperタスクを作成しています元のタスクのための

Task task = LogExceptions(() => Task.Run(() => ... 

は今wrapperタスクは常に、その内のタスクのいずれかの例外を観察します。

タスクの例外は、タイムアウトの待機を使用しているときに、呼び出し元のスレッドにマーシャリングされる場合があります。 Wait呼び出しのタイムアウトの前に例外が発生した場合、例外は呼び出しスレッドにマーシャリングされます。 Waitコールのタイムアウト後に例外が発生した場合、例外はタスクの監視されていない例外のままです。

私は例外ではなく、「マーシャリング」または「呼び出しスレッド」のあるの観点から、この考えるだろう。それだけで問題が混乱する。タスクに障害が発生すると、例外がタスクに置かれます(タスクが完了します)。したがって、待機が完了する前に例外がタスクに置かれると、タスクが完了することによって待機が満たされ、例外が発生します。タスクが完了する前に待機時間が切れた場合、待機はもはや待機していないので、もちろん例外は表示されず、例外は監視されない可能性があります。

関連する問題