C#多線程開發-任務並行庫04


你好,我是阿輝。

之前學習了線程池,知道了它有很多好處。

使用線程池可以使我們在減少並行度花銷時節省操作系統資源。可認為線程池是一個抽象層,其向程序員隱藏了使用線程的細節,使我們可以專心處理程序邏輯,而不是各種線程問題。

但也不是說我們所有的項目中都上線程池,其實它也有很多弊端,比如我們需要自定義使用異步委托的方式才可以將線程中的消息或異常傳遞出來。這些如果在一個大的軟件系統中,會導致軟件結構過於混亂,各個線程之間消息傳遞來傳遞去的,如果發生沒有處理掉的異常,很容易導致軟件出現致命錯誤。

為了解決這個問題,在.Net Framework 4.0中引入了一個新的異步操作的API,它叫任務並行庫(TPL)。

那么接下來,讓我們一起來認識一下這個TPL,看看它到底有什么魔力可以把線程池中的棘手問題解決掉。

任務並行庫

TPL又被認為是線程池的有一個抽象,其對程序員隱藏了線程池交互的底層代碼,並只提供了更方便的細粒度的API。

TPL的核心是任務。一個任務代表一個異步操作,該操作可以通過多種方式運行,可以使用或不使用獨立線程運行。

TPL有一個關鍵優勢,就是一個任務可以通過多種方式和其它任務組合起來。

比如可以同時開啟多個任務,等待所有任務完成,然后運行一個任務對之前所有任務的結果進行一些計算。

可以使用AggregateException來捕獲底層任務內部所有異常,並允許單獨處理這些異常。在C#5.0中已經內置了對TPL的支持,允許我們使用心得await和async關鍵字以平滑的、舒服的方式操作任務。

一、創建任務

可以通過下面三種方式來創建任務。

      var a1 = new Task(()=>TastMethod("線程01"));
      a1.Start();

      Task.Run(()=>TastMethod("線程001"));    
      Task.Factory.StartNew(()=>TastMethod("線程02"));
      Task.Factory.StartNew(() => TastMethod("線程03"),TaskCreationOptions.LongRunning);

      Console.ReadKey();

實例化的Tast屬性,必須進行啟動,任務才可以執行。其余的.NET已經做了內置,只需要使用就默認自動開啟。

在線程3開啟過程中,增加了TaskCreationOptions.LongRuning參數,它表示標記該任務為長時間運行,結果該任務將不會使用線程池,而在單獨的線程中運行。然而根據運行該任務的當前任務調度程序,運行方式可能不同。

二、使用任務執行基本操作

下面介紹下從任務中得到其計算法返回的結果。

        static void Main(string[] args)
        {   
            var a1 = new Task<int>(()=>TastMethod("線程01"));
            a1.Start();
            int result = a1.Result;            
            Console.WriteLine("result:" + result);
            Console.ReadKey();
        }

        static int TastMethod(string name) 
        {
            Console.WriteLine("線程名字:"+name+"Id:"+Thread.CurrentThread.ManagedThreadId+"是否屬於線程池:"+Thread.CurrentThread.IsThreadPoolThread);
            return 40;
        }

輸出結果

這里我們聲明並運行了線程01並等待結果,該任務會被放置在線程池中,並且主線程會等待,直到任務返回前一直處於阻塞狀態。

其實也可以調用方法RunSynchronously()方法,使其特定運行在主線程。這是一個非常好的優化,可以避免使用線程池來執行非常短暫的操作。

三、處理任務中的異常

在異步任務中,對於異常的處理是非常重要的。

            try
            {
                var a1 = new Task<int>(() => TastMethod("線程01",2));
                a1.Start();
                int result = a1.Result;
                Console.WriteLine("result:" + result);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }      

當程序啟動時,創建了一個任務並嘗試同步獲取任務結果。Result屬性的Get部分會使當前線程等待直到該任務結束,並將異常傳播給當前線程。此時通過try/catch是很容易捕獲到的(需要注意AggregateExceptiont,它被封裝起來,)。

int result = a1.GetAwaiter().GetResult ;

上面這種情況無需封裝異常,可以使用GetAwaiter和GetResult方法來訪問任務結果。

小寄語

人生短暫,我不想去追求自己看不見的,我只想抓住我能看的見的。

原創不易,給個關注。

我是阿輝,感謝您的閱讀,如果對你有幫助,麻煩點贊、轉發 謝謝。


免責聲明!

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



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