是否有可能等待未知的任务完成?

问题描述:

使用.NET任务并行库,是否有内置的方式来等待所有正在运行的任务完成,而没有实际参考任务?是否有可能等待未知的任务完成?

Task.WaitAny()Task.WaitAll()都需要一个Task实例的数组。我想要的是类似于Task.WaitAll()的东西,它不接受任何参数,只是等待我的应用程序产生的任何和所有任务完成。

如果没有,我会在我的框架中构建一些东西,但如果我能帮到的话,我不想重新发明轮子。

+0

通过“所有正在运行的任务,”做你的意思是你在你的进程/应用程序创建的任务? – n8wrl

+0

@ n8wrl:我说过“我的应用程序产生的任何和所有任务”......所以是的。 –

因为任何.NET代码都可以产生任何Task(即BCL,某些图书馆等),您可能不希望等待所有未完成的任务,而只是您自己的代码创建的那些任务。最简单的方法是创建自己的工厂,使用的底层一个跟踪所有未完成的任务,并暴露出所需的等待函数

public class MyTaskFactory { 
    private HashSet<Task> _tasks = new HashSet<Task>(); 

    public Task<T> StartNew<T>(Func<T> func) { 
     var t = Task.Factory.StartNew(func); 
     t.ContinueWith(x => { 
      lock(_tasks) { 
      _tasks.Remove(x); 
      } 
     }); 
     lock(_tasks) { 
      _tasks.Add(t); 
     } 
     return t; 
    } 

    // ... implement other StartNew overrides ... 

    public void WaitAll() { 
     Task[] tasks; 
     lock(_tasks) { 
      tasks = _tasks.ToArray(); 
     } 
     Task.WaitAll(tasks); 
    } 
} 
+0

我做了一些与此类似的事情,但我允许我的类保持自己的任务列表,并实现您编写的同一个WaitAll()方法,该方法由IUsesTasks接口公开。这符合我目前的框架。但是,我也喜欢你的想法,可能比我的想法更多。 +1。 –

+0

我接受了这个,因为我最终做了类似的事情,而且我喜欢't.ContinueWith(x => _tasks.Remove(x));',我没有想到。谢谢。 –

+0

此代码不是线程安全的。例如,如果'StartNew'被并行任务调用,可能会尝试对不是线程安全的容器并行调用'Remove'或'Add'。另外,如果任务在调用WaitAll期间结束,可以同时将'Remove'调用到'ToArray'。这些问题可以通过锁定或使用线程安全的集合而不是HashSet来解决,但也存在更多细微的问题:例如,如果您当前正在等待的其中一项任务调用“StartNew”,那么您会有一个“泄漏”的任务。 –

我知道这并不直接回答你的问题,但你可以改变你的架构,因此所有的任务都是作为一个“主要”任务的子节点创建的。等待这个“主要”任务将自动等待所有的孩子。

您可以创建一个子任务是这样的:

Task.Factory.StartNew(
    () => /* ... */, 
    TaskCreationOptions.AttachedToParent 
); 

您可能会发现下面的链接有用的,当它涉及到的子任务的题目是:

+0

这是一个整洁的想法 - 我会考虑它。谢谢。 +1 –