前面兩篇回顧線程和線程池的使用方法,微軟在.NET4.5推出了新的線程模型-Task。本篇將簡單的介紹Task的使用方法。 Task與線程 Task與線程或者說線程池關係緊密,可以說是基於線程池實現的,雖說任務最終還是要拋給線程去執行,但是Task仍然會比線程、線程池的開銷要小,並且提供了可靠的AP ...
前面兩篇回顧線程和線程池的使用方法,微軟在.NET4.5推出了新的線程模型-Task。本篇將簡單的介紹Task的使用方法。
Task與線程
Task與線程或者說線程池關係緊密,可以說是基於線程池實現的,雖說任務最終還是要拋給線程去執行,但是Task仍然會比線程、線程池的開銷要小,並且提供了可靠的API來控制線任務執行。
使用Task來執行的任務最終會交給線程池來執行,若該任務需要長時間執行,可以將其標記為LongRunning,這是便會單獨去請求創建線程來執行該任務。
Task
創建
Task的創建也存在兩種方式,使用new或者使用靜態工廠方式來創建:
static void Main(string[] args) { Task t = new Task(Menthod1); t.Start(); Task.Factory.StartNew(Menthod2); Console.WriteLine("主線程的ID:{0}", Thread.CurrentThread.ManagedThreadId); Console.WriteLine("--------------------"); Console.ReadLine(); } static void Menthod1() { Thread.Sleep(2000); Console.WriteLine("線程1的ID:{0}", Thread.CurrentThread.ManagedThreadId); Console.WriteLine("--------------------"); } static void Menthod2() { Thread.Sleep(4000); Console.WriteLine("線程2的ID:{0}", Thread.CurrentThread.ManagedThreadId); Console.WriteLine("--------------------"); }
上面說到使用Task執行任務最終還是需要線程池來執行,若不想讓線程池來執行,可以添加LongRunning標誌:
Task t = new Task(Menthod1, TaskCreationOptions.LongRunning); t.Start(); Task.Factory.StartNew(Menthod2, TaskCreationOptions.LongRunning);
TaskCreationOptions還有很多枚舉值,用來控制任務的更多屬性。
參數與返回值
使用Task也可以傳入參數(object類型)與返回值:
static void Main(string[] args) { Task.Factory.StartNew(Menthod1, 233); //int result = Task.Factory.StartNew(new Func<object, int>(Menthod3), 233).Result; Task<int> t = Task.Factory.StartNew(new Func<object, int>(Menthod3), 233); int result = t.Result; Console.WriteLine("主線程的ID:{0}", Thread.CurrentThread.ManagedThreadId); Console.WriteLine("result:{0}", result); Console.WriteLine("--------------------"); Console.ReadLine(); } static void Menthod1(object obj) { Thread.Sleep(2000); Console.WriteLine("線程1的ID:{0}", Thread.CurrentThread.ManagedThreadId); Console.WriteLine("obj:{0}", obj); Console.WriteLine("--------------------"); } static int Menthod3(object obj) { Thread.Sleep(6000); Console.WriteLine("線程3的ID:{0}", Thread.CurrentThread.ManagedThreadId); Console.WriteLine("obj:{0}", obj); Console.WriteLine("--------------------"); return obj; }
這裡為了獲取返回值,阻塞了主線程,這裡請註意一下。
等待Task
可以使用Task實例的Wait方法來實現等待任務結束,也可以向多線程一樣,使用WaitAll和WaitAny一樣來等待多個任務結束,不過操作更為簡單:
t.Wait();
Task.WaitAll(t1, t2 ...);
Task.WaitAny(t1, t2 ...);
取消Task
任務也是可以事先取消的,不過需要使用CancellationTokenSource:
static void Main(string[] args) { Task.Factory.StartNew(Menthod1); CancellationTokenSource cancelTokenSource = new CancellationTokenSource(); Task.Factory.StartNew(Menthod2, cancelTokenSource.Token); cancelTokenSource.Cancel(); Console.WriteLine("主線程的ID:{0}", Thread.CurrentThread.ManagedThreadId); Console.WriteLine("--------------------"); Console.ReadLine(); }
上述代碼就可以將線程2給取消掉,當然,Cancel方法可以自己找個合適的地方調用。
繼續Task
在Task中,可以實現在一個任務結束後開啟另一個新的任務:
static void Main(string[] args) { Task t = Task.Factory.StartNew(Menthod1); t.ContinueWith(new Action<Task>(Menthod4)); Console.WriteLine("主線程的ID:{0}", Thread.CurrentThread.ManagedThreadId); Console.WriteLine("任務t的ID:{0}", t.Id); Console.WriteLine("--------------------"); Console.ReadLine(); } static void Menthod1() { Thread.Sleep(2000); Console.WriteLine("線程1的ID:{0}", Thread.CurrentThread.ManagedThreadId); Console.WriteLine("--------------------"); } static void Menthod4(Task t) { Console.WriteLine("線程4的ID:{0}", Thread.CurrentThread.ManagedThreadId); Console.WriteLine("任務t的ID:{0}", t.Id); Console.WriteLine("--------------------"); }
這裡因為使用了委托,也可以使用lambda表達式,更簡單一些調用:
Task t = Task.Factory.StartNew(Menthod1); t.ContinueWith(task => { Console.WriteLine("線程4的ID:{0}", Thread.CurrentThread.ManagedThreadId); Console.WriteLine("任務t的ID:{0}", task.Id); Console.WriteLine("--------------------"); });
總結
使用傳統線程方式來進行多線程編程的時候,對線程的控制總是不到位,產生一些奇奇怪怪的問題;或是代碼寫得很雜亂;或是開發人員亂用線程,比方說無限制的創建線程、將線程池線程占滿,等等。
Task的出現,實現對傳統線程操作的封裝,提供可靠高效的API來控制線程,極大的方便多線程編程,所以可以用到Task的地方儘量使用Task;當然,這裡仍會產生線程安全的問題,同樣需要進行線程同步,與上一篇處理方式類似。
Task的內容還有很多,更深層的運行原理、封裝還等著我們去瞭解,現在只把我用到的東西分享出來,希望能幫助到需要的人。
線程的學習就先告一段落了,敬請期待新的內容吧。