C# 多線程的最佳實踐 Task


前言

  在上一篇文檔《C# 實現線程的常用幾種方式》中記錄了在C#使用多線程的常用幾種實現方式,相對來說,Task才是多線程的最佳實踐,那到底其他方式到底優缺點,而Task的優勢有哪些?下面簡單總結一下:

  Thread 類方式:

    優點:提供操作線程的API的多;能根據自己需要創建對應的線程;

    缺點:頻繁的創建和消耗比較好資源;提供操作線程的API不是馬上響應(線程是操作系統統一管理,收到指令之后,具體還得操作系統真實處理,而操作系統收到指令之后並非馬上執行相關指令);

  TreadPool 池化方式:

    優點:池化線程進行管理,需要使用就從池中獲取就行,避免頻繁創建和銷毀線程;從而可以達到線程的復用;

    缺點:提供的API太少,線程等待順序控制比較弱;從而在一些業務情況下操作不方便;

  Task:

    優點:在ThreadPool的思想進行了封裝,繼承了ThreadPool的優點;提供了豐富的線程控制API,從而方便了開發;

Task使用--創建任務的幾種方式

  方式一:

#region Task創建方式1
            //創建任務 線程離不開委托,因為需要處理業務,不然開啟線程干嘛
            Task task = new Task(() =>
            {
                Console.WriteLine($"Task 開啟線程{Thread.CurrentThread.ManagedThreadId}處理業務");
                Thread.Sleep(2000);
            });
            //開啟任務
            task.Start();
#endregion

  方式二:

#region Task創建方式2
Task task1 = Task.Run(() =>
   {
       Console.WriteLine($"Task 開啟線程{Thread.CurrentThread.ManagedThreadId}處理業務");
       Thread.Sleep(2000);
   });
#endregion

  方式三:

#region Task創建方式3
Task task2 = Task.Factory.StartNew(() =>
    {
        Console.WriteLine($"Task 開啟線程{Thread.CurrentThread.ManagedThreadId}處理業務");
        Thread.Sleep(2000);
    }); 
#endregion

Task使用--常用實例API

  常規正常流程:

static void Main(string[] args)
        {
            Console.WriteLine($"主線程{Thread.CurrentThread.ManagedThreadId}開啟");

            var task = Task.Run(() =>
            {
                Console.WriteLine($"Task 開啟線程{Thread.CurrentThread.ManagedThreadId}處理業務");
                Thread.Sleep(2000);
            });


            Console.WriteLine($"主線程{Thread.CurrentThread.ManagedThreadId}完成");
            Console.ReadKey();
}

  運行結果: 

  實例方法.Wait()

  .Wait() 等待執行調用任務完成,然后執行下一步; 及阻塞了主線程;

 

 

   實例方法.ContinueWith()

   ContinueWith() 等調用者結束之后才進行調用里面的相關業務,由線程池分配線程進行處理接下來的業務,不阻塞主線程,但卻能控制業務之間的先后順序;

 

 Task使用--Task中的靜態API

  Task.WaitAll

 

 

 Task.WaitAll等到所有任務都完成之后,才進行主線程的下一步操作,即阻塞主線程;

  Task.WaitAny

 

 

 Task.WaitAny等到其中一個任務都完成之后,才進行主線程的下一步操作,其中任務沒有完成之前也阻塞主線程;

 Task使用--Task.Factory中常用API

  Task.Factory.ContinueWhenAll 

    當ContinueWhenAll中所有任務都完成時執行回調方法,不阻塞主線程:

 static void Main(string[] args)
        {
            Console.WriteLine($"主線程{Thread.CurrentThread.ManagedThreadId}開啟");

            var task1 = Task.Run(() =>
            {
                Console.WriteLine($"Task1 開啟線程{Thread.CurrentThread.ManagedThreadId}處理業務");
                Thread.Sleep(2000);
            });
            var task2 = Task.Run(() =>
            {
                Console.WriteLine($"Task2 開啟線程{Thread.CurrentThread.ManagedThreadId}處理業務");
                Thread.Sleep(3000);
            });
            var task3 = Task.Run(() =>
            {
                Console.WriteLine($"Task3 開啟線程{Thread.CurrentThread.ManagedThreadId}處理業務");
                Thread.Sleep(5000);
            });
            List<Task> listTask = new List<Task> { task1, task2, task3 };
            Task.Factory.ContinueWhenAll(listTask.ToArray(),tasks=> {
                Console.WriteLine($"任務執行結束");

            });

            Console.WriteLine($"主線程{Thread.CurrentThread.ManagedThreadId}完成");
            Console.ReadKey();
        }

執行結果:

 

 如上圖,不阻塞主線程, 當封裝在所有list中的任務全部執行完成之后,再進行后續的處理,其中后續處理的業務的參數是上一完成任務的列表!!!

 注: 子線程開啟時,線程之間的順序是不可控制的,由操作系統根據資源情況進行分配處理;

  Task.Factory.ContinueWhenAny

    當參數中的任務有一個完成之后就進行回調,執行下一個任務。

static void Main(string[] args)
        {
            Console.WriteLine($"主線程{Thread.CurrentThread.ManagedThreadId}開啟");

            var task1 = Task.Run(() =>
            {
                Console.WriteLine($"Task1 開啟線程{Thread.CurrentThread.ManagedThreadId}處理業務");
                Thread.Sleep(2000);
            });
            var task2 = Task.Run(() =>
            {
                Console.WriteLine($"Task2 開啟線程{Thread.CurrentThread.ManagedThreadId}處理業務");
                Thread.Sleep(3000);
            });
            var task3 = Task.Run(() =>
            {
                Console.WriteLine($"Task3 開啟線程{Thread.CurrentThread.ManagedThreadId}處理業務");
                Thread.Sleep(5000);
            });
            List<Task> listTask = new List<Task> { task1, task2, task3 };
            Task.Factory.ContinueWhenAny(listTask.ToArray(), tasks =>
            {
                Console.WriteLine($"其中一個任務執行結束");

            });

            Console.WriteLine($"主線程{Thread.CurrentThread.ManagedThreadId}完成");
            Console.ReadKey();
        }

  執行結果:

 

 

 Task.Factory.ContinueWhenAny方法等其中的任務有一項完成之后就立即返回,調用后續業務,不阻塞主線程;  線程在運行過程中,之間的順序是不可控的。

CancellationTokenSource 取消任務

  在任務執行的過程中,常常會有需求進行任務的取消,有的會借用公共第三方變量進行任務控制是否繼續執行,如:true執行,false不執行;在Task中,提供了CancellationTokenSource進行任務取消。如下:

 

  如上代碼說明,在task1執行完成之后,主動調用Cancel方法取消任務,task2就檢測到任務取消,即退出;

自動取消:

 

 如上圖,通過指定一個時間,然后時間到了自動取消任務,也可以使用cancelTokenSource.CancelAfter(3000); 這樣也能實現自動取消;

總結

  以上記錄一些Task常用的API,但是沒有具體每個參數都進行記錄,但主要功能已經說明,可以根據需求自行 查看參數進行使用。


免責聲明!

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



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