在net framework 4.0中微軟又提供了一個新的非同步操作的功能,叫做任務並行庫(TPL)。任務並行庫的核心是任務(task)。一個任務代表了一個非同步操作,譔操作可以通過多種方式運行,可以使用或不使用獨立的線程。 一個任務(Task)可以通過多種方式和其他任務組合起來使用。... ...
前面我們學習了什麼是線程,線程之間的同步,使用線程池。使用線程池可以減少我們大量短時間操作的並行線程所用的操作系統資源。
在net framework 4.0中微軟又提供了一個新的非同步操作的功能,叫做任務並行庫(TPL)。任務並行庫的核心是任務(task)。一個任務代表了一個非同步操作,譔操作可以通過多種方式運行,可以使用或不使用獨立的線程。
一個任務(Task)可以通過多種方式和其他任務組合起來使用。例如,可以同時開啟多個任務,等待所有任務完成,再起一個任務進行操作。一個任務可以有多個其他任務組成,這些任務也可以依次擁有自己的子任務。
C#5.0及之後的版本都已經內置了對TPL的支持,允許我們使用await與async關鍵字進行任務執行。
以下示例,我們使用.Net Framework 4.5之後版本。
一、 創建任務
下麵的示例,我們使用task構造函數創建了兩個任務。我們傳入了一個lambda表達式做為操作任務。然後使用start啟動任務。
接著,我們使用task.Run和task.startNew方法來運行兩個任務。與使用task構造函數不同之處,在於這兩個被創建的任務會立即執行。所以無需顯式地調用 這些任務的Start方法。從task1到task4所有任務都是放線上程池中執行的,多次執行,可以發現執行順序是不一樣的。
Task5,由於我們標記為了長時間運行,所以是一個單獨的線程,不是線程池中的線程來運行的。
- 代碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace ThreadTPLDemo { class Program { static void Main(string[] args) { Console.WriteLine("Task 運行示例 ————{0}",DateTime.Now); var task1 = new Task(() => TaskOper("Task1")); var task2 = new Task(() => TaskOper("Task2")); task1.Start(); task2.Start(); Task.Factory.StartNew(() => TaskOper("Task 3")); Task.Run(() => TaskOper("Task 4")); //長時間運行 Task.Factory.StartNew(() => TaskOper("Task 5"),TaskCreationOptions.LongRunning); Thread.Sleep(1000); Console.ReadKey(); } private static void TaskOper(string name) { Console.WriteLine("Task 運行在 線程 ID:{0} 上,這個線程是不是線程池中的線程:{1},名稱: {2}", Thread.CurrentThread.ManagedThreadId,Thread.CurrentThread.IsThreadPoolThread, name); } } }
2.運行結果如下圖。我把程式運行了兩次。請自行查看不同之處。
二、 使用任務執行基本的操作
本示例是從任務中獲取結果值。我們通過不同的執行結果來顯示線上程池中執行與在主線程中執行的不同之處。
1. 代碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace ThreadTPLDemo { class Program { static void Main(string[] args) { Console.WriteLine("Task 基本操作 ————"); TaskOper("----主線程Task運行"); Task<string> task1 =CreateTask("Task1"); task1.Start(); string result = task1.Result; Console.WriteLine(" 運行結果——{0}", result); Task<string> task2 = CreateTask("Task2"); task2.RunSynchronously(); result = task1.Result; Console.WriteLine(" 運行結果——{0}", result); Task<string> task3 = CreateTask("Task3"); task3.Start(); while(!task3.IsCompleted) { Console.WriteLine(" 狀態——{0}", task3.Status); Thread.Sleep(500); } Console.WriteLine(" ——狀態—{0}", task3.Status); result = task3.Result; Console.WriteLine(" 運行結果——{0}", result); Console.ReadKey(); } private static string TaskOper(string name) { Console.WriteLine("Task 線程 ID:{0} 上,是不是線程池中的線程:{1},名稱: {2}", Thread.CurrentThread.ManagedThreadId,Thread.CurrentThread.IsThreadPoolThread, name); Thread.Sleep(2000); return string.Format("線程ID:{0},名稱:{1}", Thread.CurrentThread.ManagedThreadId,name); } static Task<string> CreateTask(string name) { return new Task<string>(() => TaskOper(name)); } } }
2.程式運行結果如下圖。
首先直接運行TaskOper方法,根據程式運行結果,我們可以知道這個方法是被同步執行的。
然後我們運行了task1,使用start方法啟動任務並等待結果。這個任務會被放線上程池中運行,而且主線程會等待,直到任務結束並返回結果。
Task2與task1相似,Task2通過RunSynchronously()方法運行的。這個任務運行在主線程中,這個任務的輸出與TaskOper方法輸出結果一樣。這就是task的優勢,可以使用task對TaskOper方法進行優化,可以避免使用線程池來執行一些執行時間非常短的操作。
Task3運行task1的方法,但是這次沒有阻塞主線程,只是在任務完成之前迴圈列印出任務狀態。