2011-08-24 86 views
3

使用.NET任務並行庫,是否有內置的方式來等待所有正在運行的任務完成,而沒有實際參考任務?是否有可能等待未知的任務完成?

Task.WaitAny()Task.WaitAll()都需要一個Task實例的數組。我想要的是類似於Task.WaitAll()的東西,它不接受任何參數,只是等待我的應用程序產生的任何和所有任務完成。

如果沒有,我會在我的框架中構建一些東西,但如果我能幫到的話,我不想重新發明輪子。

+0

通過「所有正在運行的任務,」做你的意思是你在你的進程/應用程序創建的任務? – n8wrl

+0

@ n8wrl:我說過「我的應用程序產生的任何和所有任務」......所以是的。 –

回答

4

因爲任何.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」,那麼您會有一個「泄漏」的任務。 –

5

我知道這並不直接回答你的問題,但你可以改變你的架構,因此所有的任務都是作爲一個「主要」任務的子節點創建的。等待這個「主要」任務將自動等待所有的孩子。

您可以創建一個子任務是這樣的:

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

您可能會發現下面的鏈接有用的,當它涉及到的子任務的題目是:

+0

這是一個整潔的想法 - 我會考慮它。謝謝。 +1 –