文章出處

接上文 多線程編程學習筆記——任務并行庫(一)

接上文 多線程編程學習筆記——任務并行庫(二)

 

六、   實現取消選項

          本示例學習如何實現基于Task的異步操作進行取消流程,以及在任務真正運行前如何知道任務已經被取消。

1.代碼如下:

using System;
using System.Collections.Generic;
using System.ComponentModel;
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中實現取消操作。。。");
            var cts =new CancellationTokenSource();

            var task1 = new Task<int>(() => RunTask("任務 1",10, cts.Token),cts.Token);     

                     Console.WriteLine(" ——task1 狀態—{0}", task1.Status);

            cts.Cancel();
                     Console.WriteLine(" ——取消——task1 狀態—{0}—",task1.Status);            

                     Console.WriteLine(" ——task1 在出錯之前 取消了操作—");

            //task1.Start();
            cts = new CancellationTokenSource();
            var task2 = new Task<int>(() => RunTask("任務 2", 10, cts.Token),cts.Token);
            task2.Start();
            for (int i = 0; i < 5; i++)
            {

                Thread.Sleep(500);
                Console.WriteLine(" ——task2 狀態—{0}", task2.Status);
            }

            cts.Cancel();
            for (int i = 0; i < 5; i++)
            {
                Thread.Sleep(500); 
                Console.WriteLine(" ——task2 狀態—{0}", task2.Status);

            }
            Console.WriteLine(" ——任務Task 運行結果—{0}", task2.Result);
            Thread.Sleep(2000);      
            Console.Read(); 

        }
 

        private static int RunTask(string name,int seconds,CancellationToken token)
        {      

            Console.WriteLine("Task {0}  運行在線程={1}中,是否在線程池 :{2}",name,
Thread.CurrentThread.ManagedThreadId,Thread.CurrentThread.IsThreadPoolThread);
for (int i = 0; i < seconds; i++) { Thread.Sleep(TimeSpan.FromSeconds(1)); if (token.IsCancellationRequested) { //取消操作,返回-1; return -1; } } return 42 * seconds; } } }

 

2.程序運行結果如下圖。


      首先我們來看task1的創建代碼,我們給底層任務傳遞一次取消標志,然后給任務的構造函數又傳遞了一次。

      那為什么要傳遞兩次取消標志呢?

      因為如果在task實際啟動之前取消它,則TPL的底層有責任處理這個取消操作。經過TPL底層處理過取消操作的task,如果再次啟動,則會拋出異常。如下圖。

 

 

         然后需要我們自己寫代碼處理取消操作,在取消操作之后,任務的狀態仍然是RanToCompletion,從TPL來角度來講,這個task已經完成。

 

七、   處理task中的異常

          通過此示例我們學習如何在task中拋出不同情況的異常,以及如何獲取這些異常信息。

 1.程序代碼如下:

using System;
using System.Collections.Generic;
using System.ComponentModel;
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中的異常信息。。。。。");
            try
            {
                var task1 = Task.Run(() => RunTask("任務 1", 2));

                int result = task1.Result;
                Console.WriteLine(" ——task1 狀態—{0}---值=={1}", task1.Status,result);
            }
            catch (Exception ex)
            {

                Console.WriteLine(" ——task1 錯誤信息—{0};innerException--{1}", ex.Message,
ex.InnerException==null?string.Empty:ex.InnerException.Message); } Console.WriteLine(" ——————————————————————"); try { var task2 = Task.Run(() => RunTask("任務 2", 2)); int result = task2.GetAwaiter().GetResult(); Console.WriteLine(" ——task2 狀態—{0}---值=={1}", task2.Status, result); } catch (Exception ex) { Console.WriteLine(" ——task2 錯誤信息—{0}", ex.Message); } Console.WriteLine(" ——————————————————————"); var task3 = new Task<int>(() => RunTask("任務 3", 2)); var task4 = new Task<int>(() => RunTask("任務 4", 2)); var completeTaskAll = Task.WhenAll(task3, task4); var exception = completeTaskAll.ContinueWith(t => Console.WriteLine(" ——task 錯誤信息—{0}", t.Exception)
, TaskContinuationOptions.OnlyOnFaulted); task3.Start(); task4.Start(); Thread.Sleep(
7000); Console.Read(); } private static int RunTask(string name,int seconds) { Console.WriteLine("Task {0} 運行在線程={1}中,是否在線程池 :{2}",name,
Thread.CurrentThread.ManagedThreadId,Thread.CurrentThread.IsThreadPoolThread); Thread.Sleep(TimeSpan.FromSeconds(seconds));
throw new Exception("測試錯誤信息!"); return 42 * seconds; } } }

 

2.程序運行結果,如下圖。

 

         當程序啟動時,創建一個任務task1并嘗試同步獲取結果。Result屬性的Get部分會使當前線程等待直到這個任務完成,并將異常傳播給當前線程。在這種情況下,通過catch代碼塊可以很容易地捕捉異常,不過這個異常是封裝異常。所以可以訪問InnerException來獲取異常信息。

         Task2使用GetAwaiter與GetResult來獲取任務結果。這種情況下,不需要封裝異常,TPL會提取異常。如果底層只有一個task,一次只提取一個異常。

最后一個示例是兩個任務(task3,task4)拋出異常的情況 。通過后續操作來處理異常,只有之前的任務完成之前有異常,這個后續操作才會被觸發 。通過后續操作傳遞TaskContinuationOption.OnlyOrFaulted選項來實現 ,在拋出的異常中封裝了兩個異常。


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()