簡介.NET 4.0的多工執行利器--Task

先從最簡單的開始。test1()用以另外一條Thread執行Thread.Sleep()及Console.WriteLine(),效果與ThreadPool.QueueUserWorkItem()至關。網絡

排版顯示純文字async

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Threading; using System.Diagnostics;post

namespace TaskLab { class Program { static void Main( string [] args) { test1(); Console.Read(); }測試

static  void test1()
    {
        //Task能夠代替TheadPool.QueueUserWorkItem使用
          Task.Factory.StartNew(() =>
        {
            Thread.Sleep(1000);
            Console.WriteLine( "Done!" );
        });
        Console.WriteLine( "Async Run..." );
    }
}

} StartNew()完會馬上執行下一行,故會先看到Aync Run,1秒後印出Done。spa

Async Run...Done! 同時啓動數個做業多工並行,但要等待各做業完成再繼續下一步是常見的應用情境,傳統上可透過WaitHandle、AutoResetEvent、ManualResetEvent等機制實現;Task的寫法相對簡單,創建多個Task物件,再當成Task.WaitAny()或Task.WaitAll()的參數就搞定囉!.net

排版顯示純文字pwa

static  void test2()
    {
        var task1 = Task.Factory.StartNew(() =>
        {
            Thread.Sleep(3000);
            Console.WriteLine( "Done!(3s)" );
        });
        var task2 = Task.Factory.StartNew(() =>
        {
            Thread.Sleep(5000);
            Console.WriteLine( "Done!(5s)" );
        });
        //等待任一做業完成後繼續
        Task.WaitAny(task1, task2);
        Console.WriteLine( "WaitAny Passed" );
        //等待兩項做業都完成纔會繼續執行
        Task.WaitAll(task1, task2);
        Console.WriteLine( "WaitAll Passed" );
    }

task1耗時3秒、task2耗時5秒,因此3秒後WaitAny()執行完成、5秒後WaitAll()執行完畢。code

Done!(3s)WaitAny PassedDone!(5s)WaitAll Passed 若是要等待多工做業傳回結果,透過StartNew<T>()指定傳回型別創建做業,隨後以Task.Result取值,不用額外寫Code就能確保多工做業執行完成後纔讀取結果繼續運算。blog

排版顯示純文字開發

static  void test3()
    {
        var task = Task.Factory.StartNew< string >(() =>
        {
            Thread.Sleep(2000);
            return  "Done!" ;
        });
        //使用馬錶計時
        Stopwatch sw = new Stopwatch();
        sw.Start();
        //讀task.Result時,會等到做業完畢傳回值後才繼續
          Console.WriteLine( "{0}" , task.Result);
        sw.Stop();
        //要取得task.Result耗時約2秒
Console.WriteLine( "Duration: {0:N0}ms" , sw.ElapsedMilliseconds);
    }

實際執行,要花兩秒才能跑完Console.WriteLine("{0}", task.Result),其長度就是Task執行並傳回結果的時間。

Done!Duration: 2,046ms 若是要安排多工做業完成後接連執行另外一段程式,可以使用ContinueWith():

排版顯示純文字

static  void test4()
    {
        Task.Factory.StartNew(() =>
        {
            Thread.Sleep(1000);
            Console.WriteLine( "Done!" );
        }).ContinueWith(task =>
        {
            //ContinueWith會等待前項工做完成才執行
               Console.WriteLine( "In ContinueWith" );
        });
        Console.WriteLine( "Async Run..." );
    }

如預期,ContinueWith()裏的程式會在Task完成後才被執行。

Async Run...Done!In ContinueWith .ContinueWith()傳回值還是Task物件,因此咱們能夠跟jQuery同樣玩接接樂,在ContinueWith()後方再接上另外一個ContinueWith(),各段邏輯便會依順序執行。

排版顯示純文字

static  void test5()
    {
        //ContinueWith()能夠串接
          Task.Factory.StartNew(() =>
        {
            Thread.Sleep(2000);
            Console.WriteLine( "{0:mm:ss}-Done" , DateTime.Now);
        })
        .ContinueWith(task =>
        {
            Console.WriteLine( "{0:mm:ss}-ContinueWith 1" , DateTime.Now);
            Thread.Sleep(2000);
        })
        .ContinueWith(task =>
        {
            Console.WriteLine( "{0:mm:ss}-ContinueWith 2" , DateTime.Now);
        });
        Console.WriteLine( "{0:mm:ss}-Async Run..." , DateTime.Now);
    }

Task耗時兩秒,第一個ContinueWith()耗時2秒,最後一個ContinueWith()接續在4秒後執行。

59:13-Async Run...59:15-Done59:15-ContinueWith 159:17-ContinueWith 2 最後一個例子比較複雜。ContinueWith()中的Action<Task>都會有一個輸入參數,藉以得知前一Task的執行狀態,有IsCompleted, IsCanceled, IsFaulted幾個屬性可用。

要取消執行,得藉助CancellationTokenSource及其所屬CancellationToken類別,作法是在Task中持續呼叫CancellationToken.ThrowIfCancellationRequested(),一旦外部呼叫CancellationTokenSource.Cancel(),便會觸發OperationCanceledException,Task有機制偵測此種例外情況,將結束做業執行後續的ContinueWith(),並指定Task.IsCanceled爲True覺得識別;而當Task程式發生Exception,也會結束做業觸發ContinueWith(),此時則Task.IsFaulted爲True,ContinueWith()中可透過Task.Exception.InnerExceptions取得錯誤細節。

如下程式同時可測試Task正常、取消及錯誤三種情境,使用者透過輸入1,2或3來決定要測試哪種。在Task外先宣告一個CancellationTokenSource類別,將其中的Token屬性當成StartNew()的第二項參數,而Task中則保留最初的五秒能夠取消,方法是每隔一秒呼叫一次CancellationToken.ThrowIfCancellationRequested(),當程式外部呼叫CancellationTokenSource.Cancel(),Task就會結束。5秒後若未取消,再依使用者決定的測試情境return結果或是拋出Exception。ContinueWith()則會檢查IsCanceled, IsFaulted等旗標,並輸出結果。

排版顯示純文字

static void test6() { CancellationTokenSource cts = new CancellationTokenSource(); CancellationToken cancelToken = cts.Token; Console.Write( "Test Option 1, 2 or 3 (1-Complete / 2-Cancel / 3-Fault) : " ); var key = Console.ReadKey(); Console.WriteLine(); Task.Factory.StartNew< string >(() => { //保留5秒偵測是否要Cancel for (var i = 0; i < 5; i++) { Thread.Sleep(1000); //如cancelToken.IsCancellationRequested //拋出OperationCanceledException cancelToken.ThrowIfCancellationRequested(); } switch (key.Key) { case ConsoleKey.D1: //選1時 return "OK" ; case ConsoleKey.D3: //選2時 throw new ApplicationException( "MyException" ); } return "Unknown Input" ; }, cancelToken).ContinueWith(task => { Console.WriteLine( "IsCompleted: {0} IsCanceled: {1} IsFaulted: {2}" , task.IsCompleted, task.IsCanceled, task.IsFaulted); if (task.IsCanceled) { Console.WriteLine( "Canceled!" ); } else if (task.IsFaulted) { Console.WriteLine( "Faulted!" ); foreach (Exception e in task.Exception.Flattern().InnerExceptions) { Console.WriteLine( "Error: {0}" , e.Message); } } else if (task.IsCompleted) { Console.WriteLine( "Completed! Result={0}" , task.Result); } }); Console.WriteLine( "Async Run..." ); //若是要測Cancel,2秒後觸發CancellationTokenSource.Cancel if (key.Key == ConsoleKey.D2) { Thread.Sleep(2000); cts.Cancel(); } } 如下是三種測試情境的結果。

正常執行:

Test Option 1, 2 or 3 (1-Complete / 2-Cancel / 3-Fault) : 1Async Run...IsCompleted: True IsCanceled: False IsFaulted: FalseCompleted! Result=OK 取消: (IsCanceled爲True,但留意IsCompleted也算True)

Test Option 1, 2 or 3 (1-Complete / 2-Cancel / 3-Fault) : 2Async Run...IsCompleted: True IsCanceled: True IsFaulted: FalseCanceled! 錯誤: (IsFaulted爲True,IsCompleted也是True)

Test Option 1, 2 or 3 (1-Complete / 2-Cancel / 3-Fault) : 3Async Run...IsCompleted: True IsCanceled: False IsFaulted: TrueFaulted!Error: MyException 【小結】

說穿了,Task能作的事,過去使用Thread/ThreadPool配合Event、WaitHandle同樣能辦到,但使用Task能以較簡潔的語法完成相同工做,使用.NET 4.0開發多工做業程式應可多加利用。同時,Task也是.NET 4.5 async await的基礎概念之一,值得你們花點時間熟悉,有益無害。

本文摘抄於網絡,出處http://blog.darkthread.net/post-2012-07-20-net4-task.aspx

相關文章
相關標籤/搜索