2012-02-29 3 views
2

例外をスローしないことがわかっている場合は、タスクを参照解除しても安全ですか? GCは収集する前にタスクが完了するまで待ちますか?タスクを未参照のままにしておくことは安全です

ここでは、タスクの配列を、すべてのタスクが完了したときに完了する(キャンセルまたは失敗した)タスクに変換するメソッドの例を示します。私のアプリケーションは、監視されていない例外で失敗します(タスクを使用する場所でTask.Idを記録することによって、このメソッドに提供されたタスクまたは少なくとも同じIDを持つタスクが検出されました)。私はガベージコレクタがTask.Factory.ContinueWhenAllから返されたタスクを収集することを除いて、どうしてこのようなことが起こるのか分かりません。それは、完了したときに待機しないので、配列からすべての自分のタスクを参照されずに残してしまいます。それはタスクの観察されない例外を導くでしょう。狂ったように聞こえるけど、ヘッペンには別の説明はありません。それは可能ですか?

 public static Task ToWhenAllTask(this Task[] tasks, bool cancelIfAnyCanceled = true) 
    { 
     if (tasks != null && tasks.Length == 0) 
      throw new ArgumentException(); 

     var tcs = new TaskCompletionSource<object>(); 

     Task.Factory.ContinueWhenAll(tasks, ts => { 
      try 
      { 
       List<Exception> errors = null; 
       bool canceled = false; 

       foreach (Task task in ts) 
       { 
        AggregateException ex = task.Exception; 

        if (ex != null) 
        { 
         if (errors == null) 
          errors = new List<Exception>(); 

         errors.Add(ex.Flatten()); 
        } 

        if (task.IsCanceled) 
         canceled = true; 
       } 

       if (errors != null) 
        tcs.TrySetException(errors); 
       else if (cancelIfAnyCanceled && canceled) 
        tcs.TrySetCanceled(); 
       else 
        tcs.TrySetResult(null); 
      } 
      catch(Exception ex) 
      { 

       // there is nothing to fail in this method but just in case 
       tcs.TrySetException(ex); 
      } 

     }, TaskContinuationOptions.ExecuteSynchronously); 

     return tcs.Task; 
    } 

PS。正直言って、私はタスクが完了するまで、TaskSchedullerはそれへの参照を保持していると思っていました(私の場合、継続タスクはタスク配列への参照も保持しています)。したがって、GCは、すべてのタスクが完了するまで、継続タスクとすべてのタスクをアレイから収集することはできません。

答えて

2

最後に、私の問題の理由を見つけました。私の質問への直接的な答えは最初の行に載っています - 確かにエラーで失敗しないことが分かっているなら、参照されていないタスクを残しておくことは安全です。はい、タスクが完了するまでGCで収集されませんが、タスクが開始された場合(タスクスケジューラで予定されている場合など)

上記の大文字の「BUT」は、非常に特殊なケースで問題が発生する可能性があることを意味します。タスクのインスタンスをuscheduled状態にして、すべての参照を緩めた場合。

いくつかのタスクでエラーが発生し、少なくとも1つはスケジュールされていないタスクでContinueWhenAllを実行すると、具体的な例が表示されます(残りのタスクは完了している場合)、そのすべてのタスクへの参照が緩く、ContinueWhenAllによって返された参照への参照を保存しない場合は、次回GCで収集されます。そして、ContinueWhenAllで渡されたものからの失敗したタスクは、タスクの観測されない例外を導くでしょう。

  1. ただ、新しいタスク(...)は
  2. (スタート 方法のさらなる呼び出しなし)と呼ばれていた、それが作成されました:タスクは上記のそれは方法のいずれかによって作成されたことを意味しない予定

    TaskCompleteionSourceが、 完了、失敗状態またはキャンセルに設定されていません。

このような動作は、TPLの観点から一貫しています。 ContinueWhenAllメソッドに決してスケジュールされないタスクを提供して永遠に待つべきではないからです。したがって、実際にはContinueWhenAllで渡された他のタスクがどれも失敗しても、タスクの監視されない例外が発生するのではなく、継続は決して起こりません。それでおしまい!

関連する問題