2012-02-29 59 views
2

如果我確定它不會引發異常,是否可以安全地離開任務?在收集之前GC會等到任務完成嗎?是否安全左任務unreferenced

這裏是我的方法的一個例子,它將任務數組轉換爲完成所有任務完成時完成(取消或失敗)的任務。我的應用程序因任務未被查看的異常而失敗(通過在任何我使用任務的地方記錄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收集,直到他們完成但只有在他們開始時(即他們被安排在TaskScheduler上)

上面大寫字母的'BUT'意味着對於特殊情況可能存在問題:如果您將任務的實例保留在日程安排狀態並將其全部引用釋放。

這裏是一個具體的例子(實際上我在發佈這個問題時發生了什麼),如果你對幾個任務執行ContinueWhenAll,其中一個任務失敗並且至少有一個沒有調度(其餘的是如果有任何完成),並且你鬆散地引用了所有這些任務,並且不保存對ContinueWhenAll返回的引用,那麼下一次它收集垃圾時,GC將收集它們。 ContinueWhenAll中傳遞的失敗任務將導致Task Unobserved Exception。

以上未安排的任務意味着它是由任一方式創建:

  1. 只是新的任務(...)被調用(不啓動進一步的通話 法)
  2. 它被創造TaskCompleteionSource但未設置爲 完成,失敗狀態或取消。

從TPL的角度看,這種行爲看起來一致。僅僅因爲你不應該提供永遠不會被安排到ContinueWhenAll方法並永遠等待的任務。所以事實上,如果ContinueWhenAll中沒有任何其他任務失敗,那麼繼續將不會發生,而不會發生任務未查看的異常。而已!