C# 異步編程3 TPL Task 異步程序開發


.Net在Framework4.0中增加了任務並行庫,對開發人員來說利用多核多線程CPU環境變得更加簡單,TPL正符合我們本系列的技術需求。因TPL涉及內容較多,且本系列文章為異步程序開發,所以本文並未涉及TPL全部內容。后續會寫一個TPL系列的Blog,各位朋友可以關注一下。

TASK

TPL的基礎Task,Task是TPL並行編程的最小單元,即表示一個異步操作。利用Task進行異步編程非常簡單:

        static void Main(string[] args)
        {
            BaseTaskDemo();
            //BaseTaskDemo2();//兩者效果相同
            Console.WriteLine(String.Format("Main 線程:{0}", Thread.CurrentThread.ManagedThreadId));
            Console.ReadLine();
        }
        private static void BaseTaskDemo()
        {
            var task = new Task(() => {
                Thread.Sleep(2000);
                Console.WriteLine(String.Format("Task 線程:{0}", Thread.CurrentThread.ManagedThreadId));
            });
            task.Start();
        }
        private static void BaseTaskDemo2()
        {
            var task = Task.Run(() =>
            {
                Thread.Sleep(2000);
                Console.WriteLine(String.Format("Task 線程:{0}", Thread.CurrentThread.ManagedThreadId));
            });
        }

 

程序說明:

1、new Task(Action)創建一個異步任務,參數Action是異步任務要執行的委托對象

2、task.Start()啟動異步任務的執行

3、Task.Run(Action)等效於1、2的組合

上面的示例程序實現了異步操作,但主線程無法獲知異步任務完成與否。為獲取Task的執行結果,上面的程序進行如下修改:

        static void Main(string[] args)
        {
            var task = TaskForResult();
            Console.WriteLine(String.Format("Main 線程:{0}", Thread.CurrentThread.ManagedThreadId));
            task.Wait();
            if (task.IsCompleted)
                Console.WriteLine(String.Format("獲取異步執行結果:{0}", task.Result));
            Console.ReadLine();
        }
        private static Task<int> TaskForResult()
        {
            var task = Task.Run(()=> {
                Thread.Sleep(2000);
                Console.WriteLine(string.Format("Task 線程:{0},Task執行完成。", Thread.CurrentThread.ManagedThreadId));
                return 10;
            });
            return task;
        }

程序說明:

1、下面代碼的原型為Task.Run(Func<int>),Run的參數不再是Action,因為在該任務中我們要返回一個int值,所以應該使用Run(Func<T>)這個重載。

var task = Task.Run(()=> {
    Thread.Sleep(2000);
    Console.WriteLine(string.Format("Task 線程:{0},Task執行完成。", Thread.CurrentThread.ManagedThreadId));
    return 10;
});

2、主線程中調用task.Wait()時,主線程將一直等待異步任務完成或被取消。

3、task.IsCompleted屬性用於判斷異步任務是否完成

4、task.Result獲取異步任務的執行結果(返回值)

上面的示例程序已經實現了主線程獲取異步程序的狀態及返回值,但如果異步程序非常耗時,則存在主線程需要臨時取消耗時異步程序執行的功能。為了滿足上述要求,程序可做如下調整:

        static void Main(string[] args)
        {
            CancellationTokenSource tokenSource = new CancellationTokenSource(5000);
            var task = TaskForResult2(tokenSource);
            Console.WriteLine(String.Format("Main 線程:{0}", Thread.CurrentThread.ManagedThreadId));
            Console.WriteLine(String.Format("Main 線程:{0},獲取異步執行結果:{1}", Thread.CurrentThread.ManagedThreadId, task.Result));
            Console.ReadLine();
        }
        private static Task<int> TaskForResult2(CancellationTokenSource tokenSource)
        {
            var task = Task.Run(() =>
            {
                Thread.Sleep(10000);
                if (!tokenSource.IsCancellationRequested)
                {
                    Console.WriteLine(String.Format("Task 線程:{0},任務1執行完成。", Thread.CurrentThread.ManagedThreadId));
                    return 10;
                }
                else
                {
                    return -1;
                }
            }, tokenSource.Token);
            return task;
        }

程序說明:

1、CancellationTokenSource提供任務取消消息,構造參數 5000 表示CancellationTokenSource在發出5s后超時並取消

2、在Task的委托內部 tokenSource.IsCancellationRequested 獲取取消標記

3、task.Result會隱式調用Wait()方法

如果異步Task在執行過程中出現異常,則需要對發生的異常做出響應:

        static void Main(string[] args)
        {
            CancellationTokenSource tokenSource = new CancellationTokenSource(5000);
            var task = TaskForResult2(tokenSource);
            Console.WriteLine(String.Format("Main 線程:{0}", Thread.CurrentThread.ManagedThreadId));
            try
            {
                //task.Wait();
                Console.WriteLine(String.Format("Main 線程:{0},獲取異步執行結果:{1}", Thread.CurrentThread.ManagedThreadId, task.Result));
            }
            catch (AggregateException ex)
            {

            }
            Console.ReadLine();
        }
        private static Task<int> TaskForResult2(CancellationTokenSource tokenSource)
        {
            var task = Task.Run(() =>
            {
                Thread.Sleep(1000);
                if (!tokenSource.IsCancellationRequested)
                {
                    throw new Exception("拋出異常");
                }
                else
                {
                    return -1;
                }
            }, tokenSource.Token);
            return task;
        }

程序說明:

1、在Task中引發的異常需要在 task.Wait()或task.Result時捕獲

 

寫在后面:Task的功能遠不止上述這些,如Task多任務串行、TaskFactory、Paralle等知識非常有趣和重要。如果你感興趣的話,可以關注本人后續TPL的文章。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM