2012-02-29 30 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中没有任何其他任务失败,那么继续将不会发生,而不会发生任务未查看的异常。而已!