TPL(任務並行庫)是 .NET Framework 最新版本中最有趣的新功能之一,在.NET Framework 4.0 中被引入。要使用TPL,您需要引用 System.Threading.Tasks 名稱空間。
什麼是 task schedulers?為什麼需要它們?如果我想問,Task 是如何被排程執行的?其實有一個元件叫做 task scheduler,它專門負責排程 task。從本質上來說,這是一個底層物件的抽象層,它可以將您的 task 透過排隊排程到執行緒上。
.NET Framework 為您提供了兩個 task schedulers。包括基於預設的 task scheduler 用於排程在 Thread Pool 之上,另一個 task scheduler 能夠排程在 指定物件的同步上下文上。請注意,TPL的預設 task scheduler 利用了.NET Framework 執行緒池。該執行緒池的表示類 ThreadPool 是在 System.Threading.Tasks名稱空間下。
儘管預設的 task scheduler 在大多數情況下已足夠,但有時候你也許也想構建自定義的 task scheduler 去完成個性化的功能,比如那些預設的 task scheduler 沒有提供的。此類功能可能包括 FIFO執行,併發度等。
在C#中擴充套件 TaskScheduler要構建自定義的 task scheduler,您需要建立一個類並繼承 System.Threading.Tasks.TaskScheduler 。因此,要構建自定義的 task scheduler,您需要擴充套件 TaskScheduler抽象類並重寫以下方法。
QueueTask 返回void並接受Task物件作為引數,當一個 task 需要排程的時候就要呼叫這個方法GetScheduledTasks 返回所有已被呼叫的 task list, (準確的說是IEnumerable)TryExecuteTaskInline 用於內聯方式(即在當前執行緒上)執行task。在這種情況下,無需排隊即可執行 task下面的程式碼段展示瞭如何繼承 TaskScheduler 類來實現我們自定義的 scheduler。
public class CustomTaskScheduler : TaskScheduler, IDisposable { }
正如在本文前面所討論的,您需要在 CustomTaskScheduler 中重寫下面3個方法: GetScheduledTasks,QueueTask和TryExecuteTaskInline。
public sealed class CustomTaskScheduler : TaskScheduler, IDisposable { protected override IEnumerable<Task> GetScheduledTasks() { //TODO } protected override void QueueTask(Task task) { //TODO } protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) { //TODO } public void Dispose() { //TODO } }
使用 BlockingCollection 來儲存 task物件集合
現在我們開始實現自定義的 task scheduler。以下程式碼段顯示瞭如何利用 BlockingCollection 儲存 task物件的集合。
public sealed class CustomTaskScheduler : TaskScheduler, IDisposable { private BlockingCollection<Task> tasksCollection = new BlockingCollection<Task>(); private readonly Thread mainThread = null; public CustomTaskScheduler() { mainThread = new Thread(new ThreadStart(Execute)); if (!mainThread.IsAlive) { mainThread.Start(); } } private void Execute() { foreach (var task in tasksCollection.GetConsumingEnumerable()) { TryExecuteTask(task); } } //Other methods }
可以著重看看 CustomTaskScheduler 類的建構函式,一個 thread 是如何被建立並且如何開始排程 Execute 方法的。
實現 GetScheduledTasks,QueueTask 和 TryExecuteTaskInline接下來,我們需要在 CustomTaskScheduler 中實現三個自定義方法。這三種方法包括 GetScheduledTasks,QueueTask 和 TryExecuteTaskInline。
GetScheduledTasks方法返回 IEnumerable 型別的 task 集合,這樣你就可以迭代這個 collection,就像上面 Execute 方法一樣。QueueTask方法接受Task物件作為方法引數,然後將其儲存在 task collection 中,TryExecuteTaskInline方法暫不實現, 我準備將它留給讀者來實現。
protected override IEnumerable<Task> GetScheduledTasks() { return tasksCollection.ToArray(); } protected override void QueueTask(Task task) { if (task != null) tasksCollection.Add(task); } protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) { return false; }
完整的 CustomTaskScheduler 程式碼如下下面是最終版本的 CustomTaskScheduler 程式碼清單:
public sealed class CustomTaskScheduler : TaskScheduler, IDisposable { private BlockingCollection<Task> tasksCollection = new BlockingCollection<Task>(); private readonly Thread mainThread = null; public CustomTaskScheduler() { mainThread = new Thread(new ThreadStart(Execute)); if (!mainThread.IsAlive) { mainThread.Start(); } } private void Execute() { foreach (var task in tasksCollection.GetConsumingEnumerable()) { TryExecuteTask(task); } } protected override IEnumerable<Task> GetScheduledTasks() { return tasksCollection.ToArray(); } protected override void QueueTask(Task task) { if (task != null) tasksCollection.Add(task); } protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) { return false; } private void Dispose(bool disposing) { if (!disposing) return; tasksCollection.CompleteAdding(); tasksCollection.Dispose(); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } }
要使用我們剛剛實現的 CustomTaskScheduler ,您可以使用以下程式碼段: