NETCORE - TASK多線程的使用


NETCORE - TASK多線程的使用

 Task是從 .NET Framework 4 開始引入的一項基於隊列的異步任務(TAP)模式,從 .NET Framework 4.5 開始,任何使用 async/await 進行修飾的方法,都會被認為是一個異步方法;實際上,這些異步方法都是基於隊列的線程任務,從你開始使用 Task 去運行一段代碼的時候,實際上就相當於開啟了一個線程,默認情況下,這個線程數由線程池 ThreadPool 進行管理的。

 

1. Task 的使用方法 

       /// <summary>
        /// 最簡單的使用方式
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        [Route("GetTask")]
        public IActionResult GetTask()
        {
            Console.ForegroundColor = ConsoleColor.Red;

            // 執行一個無返回值的任務
            Task.Run(() => { Console.WriteLine("runing ..."); });

            // 執行一個返回 int 類型結果的任務
            var res1 = Task.Run<int>(() => { return 483; });

            // 聲明一個任務,僅聲明,不執行
            Task t = new Task(() => { Console.WriteLine("聲明"); });

            Console.ResetColor();

            return Ok("test");
        }

 

 

 

2. 使用 TaskFactory 工廠開始異步任務 

        /// <summary>
        /// 使用 TaskFactory 工廠開始異步任務
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        [Route("GetTask2")]
        public IActionResult GetTask2()
        {
            Console.ForegroundColor = ConsoleColor.Red;

            List<Task<int>> tasks = new List<Task<int>>();

            TaskFactory factory = new TaskFactory();

            tasks.Add(factory.StartNew<int>(() => { Console.WriteLine("t1"); return 1; }));
            tasks.Add(factory.StartNew<int>(() => { Console.WriteLine("t2"); return 2; }));
            tasks.Add(factory.StartNew<int>(() => { Console.WriteLine("t3"); return 3; }));

            tasks.ForEach(t => Console.WriteLine("Task:{0}", t.Result));

            Console.ResetColor();

            return Ok("test2");
        }

 

 

 上面的代碼使用 TaskFactory 創建並運行了兩個異步任務,同時把這兩個任務加入了任務列表 tasks 中,然后立即迭代此 tasks 獲取異步任務的執行結果,使用 TaskFactory 工廠類,可以創建一組人物,然后依次執行它們

 

 

3. 處理 Task 中的異常

        /// <summary>
        ///  處理 Task 中的異常
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        [Route("GetTask3")]
        public IActionResult GetTask3()
        {
            Console.ForegroundColor = ConsoleColor.Red;

            var task = Task.Run(() =>
            {
                Console.WriteLine("SimpleTask");
                Task.Delay(1000).Wait();
                throw new Exception("SimpleTask Error");
            });

            try
            {
                task.Wait();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

            if (task.IsCompletedSuccessfully)//任務成功
            {
                Console.WriteLine("IsCompletedSuccessfully");
            }

            if (task.IsCompleted)//任務完成
            {
                Console.WriteLine("IsCompleted");
            }


            Console.ResetColor();

            return Ok("test2");
        }

 

 異步任務中發生異常會導致任務拋出 TaskCancelException 的異常,僅表示任務退出,程序應當捕獲該異常;然后,立即調用 Task 進行狀態判斷,獲取內部異常 

上面的代碼模擬了 Task 內部發生的異常,並捕獲了異常,通常情況下,推薦使用 Task 的任務狀態判斷以進行下一步的任務處理(如果需要),如果僅僅是簡單的執行一個異步任務,直接捕獲異常即可,這里使用了狀態判斷,如果任務已完成,則打印一則消息:IsCompleted;很明顯,在上面的代碼中,此 “IsCompleted” 消息並不會被打印到控制台
注意,這里使用了 task.IsCompletedSuccessfully 而不是 task.IsCompleted,這兩者的區別在於,前者只有在任務正常執行完成,無異常,無中途退出指令的情況下才會表示已完成,而 task.IsCompleted 則僅僅表示“任務完成” 

 

 

4. 同步上下文

 在 WinForm/WPF 應用程序中,也常常需要在 UI 上開辟異步任務,通常情況下,窗體控件僅允許創建其的線程訪問,在沒有 Task 的時代,處理異步上下文到同步上下文是一件非常復雜的事情,在 Task 出現以后,提供了 TaskScheduler 任務調度器,讓我們可以非常方便的在異步線程中訪問 UI 線程的資源

        /// <summary>
        /// 獲取當前線程上下文對象
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        [Route("GetTask4")]
        public IActionResult GetTask4()
        {
            Console.ForegroundColor = ConsoleColor.Red;

            SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());

            var UISyncContext = TaskScheduler.FromCurrentSynchronizationContext();

            var t1 = Task.Factory.StartNew(() => { return 1; });

            t1.ContinueWith((atnt) => { Console.WriteLine("從這里訪問 UI 線程的資源"); }, UISyncContext);

            Console.ResetColor();

            return Ok("test2");
        }

 

 從上面的代碼可以發現,僅僅需要調用 TaskScheduler.FromCurrentSynchronizationContext() 獲得當前線程的同步上下文,然后在執行異步任務的時候傳入,即可訪問當前線程創建的 UI 資源

 

5. Task 的運行方式

 基於 ThreadPool 線程池的方式

        /// <summary>
        ///基於 ThreadPool 線程池的方式
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        [Route("GetTask5")]
        public IActionResult GetTask5()
        {
            Console.ForegroundColor = ConsoleColor.Red;

            var available = ThreadPool.SetMaxThreads(8, 16);
            Console.WriteLine("result:{0}", available);

            Console.ResetColor();

            return Ok("test2");
        }

 

 一個異步任務總是處於隊列中,任務隊列基於先進先出的原則,最先進入隊列的任務總是最先被執行;但是,在多線程的環境下,最先執行並不意味着最先結束,意識到這一點很重要,每個任務可調度的資源和處理的進度決定了任務的完成時間。
默認情況下,所有的任務都使用 ThreadPool 的資源,當你開啟一個 Task 的時候,實際上,是由 ThreadPool 分配了一個線程,ThreadPool 的上限取決於很多方面的因素,例如虛擬內存的大小,當 Task 開啟的數量超過ThreadPool 的上限的時候,Task 將進入排隊狀態,可以手動設置 ThreadPool 的大小

 

 上面的代碼表示設置當前程序可使用的線程池大小,但是,SetMaxThreads 的值不應該小於托管服務器的 CPU 核心數量,否則,變量 available 的值將顯示為 false,表示未成功設置線程池上限
注意:ThreadPool 上的所有線程都是后台線程,也就是說,其IsBackground屬性是true,在托管程序退出后,ThreadPool 也將會退出。

 

6. 長時間運行於后台的任務

        /// <summary>
        /// 長時間運行於后台的任務
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        [Route("GetTask6")]
        public IActionResult GetTask6()
        {
            Console.ForegroundColor = ConsoleColor.Red;

            Task.Factory.StartNew(() => { Console.WriteLine("LongRunging Task"); }, TaskCreationOptions.LongRunning);
            Console.ResetColor();

            return Ok("test2");
        }

 在創建 Task 的時候,我們可能需要做一些長時間運行的業務,這個時候如果使用默認的 ThreadPool 資源,在並發狀態下,這是不合適的,因為該任務總是長時間的占用線程池中的資源,導致線程池數量受限,這種情況下,可以在創建任務的時候使用指定 TaskCreationOptions.LongRunning 方式創建 Task

 

7.有條件的 Task

     /// <summary>
        /// 有條件的 Task
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        [Route("GetTask7")]
        public IActionResult GetTask7()
        {
            Console.ForegroundColor = ConsoleColor.Red;

            //var order1 = Task.Run<string>(() => { Console.WriteLine("Order 1 "); return "order1 result"; });

            //// 匿名委托將等待 order1 執行完成后執行,並將 order1 對象作為參數傳入
            //order1.ContinueWith((task) => { Console.WriteLine("order1 is completed! {0}", task.Result); });

            //var t1 = Task.Run(() => { Task.Delay(1500).Wait(); Console.WriteLine("t1"); });
            //var t2 = Task.Run(() => { Task.Delay(2000).Wait(); Console.WriteLine("t2"); });
            //var t3 = Task.Run(() => { Task.Delay(3000).Wait(); Console.WriteLine("t3"); });

            //Task.WaitAll(t1, t2, t3);

            //// t1,t2,t3 完成后輸出下面的消息
            //Console.WriteLine("t1,t2,t3 Is Complete");

            //var t4 = Task.Run(() => { Task.Delay(1500).Wait(); Console.WriteLine("t4"); });
            //var t5 = Task.Run(() => { Task.Delay(2000).Wait(); Console.WriteLine("t5"); });
            //var t6 = Task.Run(() => { Task.Delay(3000).Wait(); Console.WriteLine("t6"); });
            //Task.WaitAny(t4, t5, t6);
            //// 當任意任務完成時,輸出下面的消息,目前按延遲時間計算,在 t4 完成后立即輸出下面的信息
            //Console.WriteLine("t4,t5,t6 Is Complete");

            //var t7 = Task.Run(() => { Task.Delay(1500).Wait(); Console.WriteLine("t7"); });
            //var t8 = Task.Run(() => { Task.Delay(2000).Wait(); Console.WriteLine("t8"); });
            //var t9 = Task.Run(() => { Task.Delay(3000).Wait(); Console.WriteLine("t9"); });
            //var whenAll = Task.WhenAll(t7, t8, t9);
            //// WhenAll 不會等待,所以這里必須顯示指定等待
            //whenAll.Wait();
            //// 當所有任務完成時,輸出下面的消息
            //Console.WriteLine("t7,t8,t9 Is Complete");

            var t10 = Task.Run(() => { Task.Delay(1500).Wait(); Console.WriteLine("t10"); });
            var t11 = Task.Run(() => { Task.Delay(2000).Wait(); Console.WriteLine("t11"); });
            var t12 = Task.Run(() => { Task.Delay(3000).Wait(); Console.WriteLine("t12"); });
            var whenAny = Task.WhenAll(t10, t11, t12);
            // whenAny 不會等待,所以這里必須顯示指定等待
            whenAny.Wait();
            // 當任意任務完成時,輸出下面的消息,目前按延遲時間計算,在 t10 完成后立即輸出下面的信息
            Console.WriteLine("t10,t11,t12 Is Complete");

            Console.ResetColor();

            return Ok("test2");
        }

 

Task 內部提供多種多樣的基於隊列的鏈式任務管理方法,通過使用這些快捷方式,可以讓異步隊列有序的執行,比如ContinueWith(),ContinueWhenAll(),ContinueWhenAny(),WaitAll(),WaitAny(),WhenAll(),WhenAny()

 

 

值得注意的是,當調用 WhenAll 方法時,會返回執行任務的狀態,此狀態是所有任務的統一狀態,如果執行了 3 個任務,而其中一個出錯,則返回任務狀態表示為:Faulted,如果任意任務被取消,則狀態為:Canceled;
當調用 WhenAny() 方法時,表示任意任務完成即可表示完成,此時,會返回最先完成的任務信息
注意:WhenAll 和 WhenAny 方法正常執行,無異常,無取消,則所返回的完成狀態表示為:RanToCompletion

 

 

 

 

項目:NETCORE.TASKRORD
附代碼:https://gitee.com/wuxincaicai/NETCORE.git

引用:https://www.cnblogs.com/viter/p/10201228.html

 


免責聲明!

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



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