Asp.Net Core 輕松學-多線程之Task(補充)


前言

    在上一章 Asp.Net Core 輕松學-多線程之Task快速上手 文章中,介紹了使用Task的各種常用場景,但是感覺有部分內容還沒有完善,在這里補充一下。

1. 任務的等待

在使用 Task 進行基於隊列的異步任務(TAP)的時候,對於剛入門的同學來說,只是簡單的了解了使用 Task 可以在后台處理異步任務,但是對於阻塞調用可能還有有一些不太明白,異步任務默認是不阻塞的執行過程,當一個 Task 被創建出來的時候,並沒有被壓入隊列中,而是開始執行的時候,才會進入隊列中;執行一個異步任務可以設置等待

1.1 使用 await 進行等待任務完成
    [HttpGet]
     public async Task<string> Get()
        {
            string result = string.Empty;
            await Task.Run(() =>
             {
                 result = "Hello World!";
             });
            return result;
        }

上面的異步方法 Get() 內部執行了一個 Task,為了保持方法在返回的時候能夠給變量 result 設置值,這里使用了 await 等待任務完成,在任務未完成前,即 result 未被設置值 Hello World! 前,該接口將一直阻塞,直到任務完成。

1.1 使用 Wait()
        [HttpGet("WaitTest")]
        public string WaitTest(int id)
        {
            string result = string.Empty;
            var waitTask = Task.Run(() =>
             {
                 result = "Hello World!";
             });
            waitTask.Wait(TimeSpan.FromSeconds(5));

            return result;
        }

上面的代碼定義了一個 TAP ,waitTask 使用了 Wait() 方法進行等待並傳入一個時間,表示等待 5 秒中后退出阻塞過程,

1.2 使用 CancellationToken 方法
        [HttpGet("WaitToken")]
        public string WaitToken(int id)
        {
            var result = string.Empty;
            CancellationTokenSource cts = new CancellationTokenSource();
            var taskToken = Task.Run(() =>
            {
                cts.CancelAfter(TimeSpan.FromSeconds(1));
                Task.Delay(2000).Wait();
                result = "Hello World!";
            });
            taskToken.Wait(cts.Token);

            return result;
        }

上面的任務 taskToken ,則使用了取消令牌,當令牌沒有收到取消通知的時候,該任務將一直等待, taskToken 任務內部指示取消令牌 1 秒后取消,同時,任務內部使用 Task.Delay 阻塞 2 秒,這很特別,這種設置使得 taskToken 任務將引發任務取消的異常而導致無法給 result 變量進行值設置,如果你對取消令牌不太了解,建議閱讀我之前的文章 Asp.Net Core 輕松學-多線程之取消令牌

2. 同步方法中的異步任務

在同步方法中,我們可以非常容易的創建一個 Task 任務,特別是 .Net Core 提供了 Task 這么方便的使用方式的情況下,在某些場景下,就會出現一些意想不到的問題,我的忠告是:在使用 TAP 的時候,盡可能的使用可等待的任務,特別是需要注意不要在 TAP 中運行一些耗時較長的任務,比如批量處理數據、事務過程等等,如果真的是需要有這一類型的任務需要使用 Task 進行處理,那么我的建議是,將這些任務縮小,然后盡快的結束,比如,你可以使用消息隊列來執行批量處理數據,而 Task 的任務則縮小至把處理條件丟到消息隊列中,這樣的解耦才是正確的選擇,長時間運行於后台的 TAP,常常導致許多問題,比如消息冒泡、服務重啟,都有可能會中斷 TAP 線程,要知道,所有的 TAP 都是后台線程,當主進程退出的時候,后台線程也將被清理

3. 手動排隊任務

在 TheadPool 內部,提供了一個排隊的方法,當線程池資源可用后,將會自動的執行該隊列,這樣做的好處顯而易見,就是你可以通過定義一系列的任務,然后等待線程池去按順序處理它,這個排隊的過程本質上就是隊列

3.1 手動排隊任務
        [HttpGet("TaskQueue")]
        public bool TaskQueue()
        {
            var inQueues = ThreadPool.QueueUserWorkItem(ThreadProc);

            return inQueues;
        }

        private void ThreadProc(Object stateInfo)
        {
            Console.WriteLine("此任務來自線程池隊列執行");
        }

上面的代碼中,在 TaskQueue() 內部使用 ThreadPool.QueueUserWorkItem() 將方法 ThreadProc(Object stateInfo) 壓入隊列中,並返回一個值:inQueues ,該值指示任務壓入隊列是否成功,在 .Net Core 中,ThreadPool.QueueUserWorkItem() 提供了 3 個方法重載,可以按需使用;使用重載方法,甚至可以在壓入任務的時候傳入參數調用,類似下面的代碼

3.2 在排隊任務時傳遞參數
        [HttpGet("TaskQueue")]
        public bool TaskQueue()
        {
            var inQueues = ThreadPool.QueueUserWorkItem(ThreadProc);

            var inQueuesSecond = ThreadPool.QueueUserWorkItem(ThreadProc, "這是一條測試消息");

            return inQueues;
        }

        private void ThreadProc(Object stateInfo)
        {
            Console.WriteLine(stateInfo);
        }

如果業務需要,該參數還可傳入實體對象

4. 混合方法(Hybrid Approach)

使用混合方法執行 TAP 的好處是,可以隱藏業務實現細節,提供統一調用入口

4.1 定義混合方法
        [HttpGet("HyBrid")]
        public Task<string> HyBrid([FromQuery]string value)
        {
            return  HyBridInternal(value);
        }

        private async Task<string> HyBridInternal(string value)
        {
            await Task.Run(() =>
            {
                Console.WriteLine(value);
            });

            return "Your Input:" + value;
        }

混合方法,不要被它的名頭唬住,你可以把它看成一個方法重載,這樣做的好處是,當發生異常是,你可以快速的定位到出現異常的方法,而不是任務

結束語

本文的內容只是上一篇文章的補充,所以這里就不在放入執行結果,但是示例代碼還是一樣的奉上

示例代碼下載

https://github.com/lianggx/EasyAspNetCoreDemo/tree/master/Ron.TaskSecond


免責聲明!

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



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