日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > C# >内容正文

C#

《CLR Via C# 第3版》笔记之(十九) - 任务(Task)

發布時間:2025/3/20 C# 54 豆豆
生活随笔 收集整理的這篇文章主要介紹了 《CLR Via C# 第3版》笔记之(十九) - 任务(Task) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

除了上篇中提到的線程池,本篇介紹一種新的實現異步操作的方法--任務(Task)。

主要內容:

  • 任務的介紹
  • 任務的基本應用
  • 子任務和任務工廠
  • 任務調度器
  • 并行任務Parallel

?

1. 任務的介紹

利用ThreadPool的QueueUserWorkItem方法建立的異步操作存在一些限制:

  • 異步操作沒有返回值
  • 沒有內建的機制來通知異步操作什么時候完成
  • ?

    而使用任務(Task)來建立異步操作可以克服上述限制,同時還解決了其他一些問題。

    任務(Task)對象和線程池相比,多了很多狀態字段和方法,便于更好的控制任務(Task)的運行。

    當然,任務(Task)提供大量的功能也是有代價的,意味著更多的內存消耗。所以在實際使用中,如果不用任務(Task)的附加功能,那么就使用ThreadPool的QueueUserWorkItem方法。

    ?

    通過任務的狀態(TaskStatus),可以了解任務(Task)的生命周期。

    TaskStatus是一個枚舉類型,定義如下:

    public enum TaskStatus { // 運行前狀態Created = 0, // 任務被顯式創建,通過Start()開始這個任務WaitingForActivation = 1, // 任務被隱式創建,會自動開始WaitingToRun = 2, // 任務已經被調度,但是還沒有運行// 運行中狀態Running = 3, // 任務正在運行WaitingForChildrenToComplete = 4, // 等待子任務完成// 運行完成后狀態RanToCompletion = 5, // 任務正常完成Canceled = 6, // 任務被取消Faulted = 7, // 任務出錯 }

    構造一個Task后,它的狀態為Create

    啟動后,狀態變為WaitingToRun

    實際在一個線程上運行時,狀態變為Running

    運行完成后,根據實際情況,狀態變為RanToCompletiionCanceledFaulted三種中的一種。

    如果Task不是通過new來創建的,而是通過以下某個函數創建的,那么它的狀態就是WaitingForActivation

    ContinueWithContinueWhenAllContinueWhenAnyFromAsync。

    如果Task是通過構造一個TaskCompletionSource<TResult>對象來創建的,該Task在創建時也是處于WaitingForActivation狀態。

    ?

    2. 任務的基本應用

    下面演示任務的創建,取消,等待等基本使用方法。

    2.1 創建并啟動一個Task

    using System; using System.Threading.Tasks; using System.Threading;public class CLRviaCSharp_19 {static void Main(string[] args){Console.WriteLine("Main Thread start!");// 創建一個TaskTask t1 = new Task(() => { Console.WriteLine("Task start"); Thread.Sleep(1000);Console.WriteLine("Task end");});// 啟動Taskt1.Start();// 主線程并沒有等待Task,在Task完成前就已經完成了Console.WriteLine("Main Thread end!");Console.ReadKey(true);} }

    ?

    2.2 主線程等待子線程完成

    using System; using System.Threading.Tasks; using System.Threading;public class CLRviaCSharp_19 {static void Main(string[] args){Console.WriteLine("Main Thread start!");// 創建2個TaskTask t1 = new Task(() => { Console.WriteLine("Task1 start"); Thread.Sleep(1000);Console.WriteLine("Task1 end");});Task t2 = new Task(() =>{Console.WriteLine("Task2 start");Thread.Sleep(2000);Console.WriteLine("Task2 end");});// 啟動Taskt1.Start();t2.Start();// 當t1和t2中任何一個完成后,主線程繼續后面的操作// Task.WaitAny(new Task[] { t1, t2 });// 當t1和t2中全部完成后,主線程繼續后面的操作Task.WaitAll(new Task[] { t1, t2 });Console.WriteLine("Main Thread end!");Console.ReadKey(true);} }

    等待的方法WaitAllWaitAny可根據應用場景選用一個。

    ?

    2.3 取消Task

    取消Task和取消一個線程類似,使用CancellationTokenSource

    using System; using System.Threading.Tasks; using System.Threading;public class CLRviaCSharp_19 {static void Main(string[] args){Console.WriteLine("Main Thread start!");CancellationTokenSource cts = new CancellationTokenSource();// 創建2個TaskTask t1 = new Task(() => { Console.WriteLine("Task1 start");for (int i = 0; i < 100; i++){if (!cts.Token.IsCancellationRequested){Console.WriteLine("Count : " + i.ToString());Thread.Sleep(1000);}else{Console.WriteLine("Task1 is Cancelled!");break;}}Console.WriteLine("Task1 end");}, cts.Token);// 啟動Taskt1.Start();Thread.Sleep(3000);// 運行3秒后取消Taskcts.Cancel();// 為了測試取消操作,主線程等待Task完成Task.WaitAny(new Task[] { t1 });Console.WriteLine("Main Thread end!");Console.ReadKey(true);} }

    ?

    3. 子任務和任務工廠

    3.1 延續任務

    為了保證程序的伸縮性,應該盡量避免線程阻塞,這就意味著我們在等待一個任務完成時,最好不要用Wait,而是讓一個任務結束后自動啟動它的下一個任務。

    using System; using System.Threading.Tasks; using System.Threading;public class CLRviaCSharp_19 {static void Main(string[] args){Console.WriteLine("Main Thread start!");// 第一個TaskTask<int> t1 = new Task<int>(() =>{Console.WriteLine("Task 1 start!");Thread.Sleep(2000);Console.WriteLine("Task 1 end!");return 1;});// 啟動第一個Taskt1.Start();// 因為TaskContinuationOptions.OnlyOnRanToCompletion,// 所以第一個Task正常結束時,啟動第二個Task。// TaskContinuationOptions.OnlyOnFaulted,則第一個Task出現異常時,啟動第二個Task// 其他可詳細參考TaskContinuationOptions定義的各個標志t1.ContinueWith(AnotherTask, TaskContinuationOptions.OnlyOnRanToCompletion);Console.WriteLine("Main Thread end!");Console.ReadKey(true);}// 第二個Task的處理都在AnotherTask函數中,// 第二個Task的引用其實就是上面ContinueWith函數的返回值。// 這里沒有保存第二個Task的引用private static void AnotherTask(Task<int> task){Console.WriteLine("Task 2 start!");Thread.Sleep(1000);Console.WriteLine("Task 1's return Value is : " + task.Result);Console.WriteLine("Task 2 end!");} }

    ?

    3.2 子任務

    定義子任務時,注意一定要加上TaskCreationOptions.AttachedToParent,這樣父任務會等待子任務執行完后才結束。

    using System; using System.Threading.Tasks; using System.Threading;public class CLRviaCSharp_19 {static void Main(string[] args){Console.WriteLine("Main Thread start!");Task<int[]> parentTask = new Task<int[]>(() =>{var result = new int[3];// 子任務1new Task(() => { Console.WriteLine("sub task 1 start!"); Thread.Sleep(1000);Console.WriteLine("sub task 1 end!");result[0] = 1;}, TaskCreationOptions.AttachedToParent).Start();// 子任務2new Task(() =>{Console.WriteLine("sub task 2 start!");Thread.Sleep(1000);Console.WriteLine("sub task 2 end!");result[1] = 2;}, TaskCreationOptions.AttachedToParent).Start();// 子任務3new Task(() =>{Console.WriteLine("sub task 3 start!");Thread.Sleep(1000);Console.WriteLine("sub task 3 end!");result[2] = 3;}, TaskCreationOptions.AttachedToParent).Start();return result;});parentTask.Start();Console.WriteLine("Parent Task's Result is :");foreach (int result in parentTask.Result)Console.Write("{0}\t", result);Console.WriteLine();Console.WriteLine("Main Thread end!");Console.ReadKey(true);} }

    上面的例子中,可以把TaskCreationOptions.AttachedToParent刪掉試試,打印出來的Result應該是3個0,而不是1? 2?? 3

    3個子任務的執行順序也和定義的順序無關,比如任務3可能最先執行(與CPU的調度有關)。

    ?

    3.3 任務工廠

    除了上面的方法,還可以使用任務工廠來批量創建任務。

    using System; using System.Threading.Tasks; using System.Threading;public class CLRviaCSharp_19 {static void Main(string[] args){Console.WriteLine("Main Thread start!");Task<int[]> parentTask = new Task<int[]>(() =>{var result = new int[3];TaskFactory tf = new TaskFactory(TaskCreationOptions.AttachedToParent, TaskContinuationOptions.None);// 子任務1tf.StartNew(() =>{Console.WriteLine("sub task 1 start!");Thread.Sleep(1000);Console.WriteLine("sub task 1 end!");result[0] = 1;});// 子任務2tf.StartNew(() =>{Console.WriteLine("sub task 2 start!");Thread.Sleep(1000);Console.WriteLine("sub task 2 end!");result[1] = 2;});// 子任務3tf.StartNew(() =>{Console.WriteLine("sub task 3 start!");Thread.Sleep(1000);Console.WriteLine("sub task 3 end!");result[2] = 3;});return result;});parentTask.Start();Console.WriteLine("Parent Task's Result is :");foreach (int result in parentTask.Result)Console.Write("{0}\t", result);Console.WriteLine();Console.WriteLine("Main Thread end!");Console.ReadKey(true);} }

    使用任務工廠與上面3.2中直接定義子任務相比,優勢主要在于可以共享子任務的設置,比如在TaskFactory中設置了TaskCreationOptions.AttachedToParent,那么它啟動的子任務都具有這個屬性了。

    當然,任務工廠(TaskFactory)還提供了很多控制子任務的函數,用的時候可以看看它的類定義。

    ?

    4. 任務調度器

    上面例子中任務的各種操作(運行,等待,取消等等),都是由CLR的任務調度器來調度的。

    ?

    FCL公開了2種任務調度器:線程池任務調度器同步上下文任務調度器

    默認情況下,應用程序都是使用的線程池任務調度器。WPF和Winform中通常使用同步上下文任務調度器

    ?

    CLR的任務調度器類(TaskScheduler)中有個Default屬性返回的就是線程池任務調度器

    還有個FromCurrentSynchronizationContext方法,返回的是同步上下文任務調度器

    ?

    我們也可以通過繼承CLR中的任務調度器(TaskScheduler)來定制適合自己業務需要的任務調度器。

    下面我們定制一個簡單的TaskScheduler,將3.3中每個子任務的打印信息的功能移到自定義的任務調度器MyTaskScheduler中。

    using System; using System.Threading.Tasks; using System.Threading; using System.Collections.Generic;public class CLRviaCSharp_19 {static void Main(string[] args){Console.WriteLine("Main Thread start!");Task<int[]> parentTask = new Task<int[]>(() =>{var result = new int[3];// 這里的TaskFactory中指定的是自定義的任務調度器MyTaskSchedulerTaskFactory tf = new TaskFactory(CancellationToken.None, TaskCreationOptions.AttachedToParent,TaskContinuationOptions.None, new MyTaskScheduler());// 子任務1tf.StartNew(() =>{Thread.Sleep(1000);result[0] = 1;});// 子任務2tf.StartNew(() =>{Thread.Sleep(1000);result[1] = 2;});// 子任務3tf.StartNew(() =>{Thread.Sleep(1000);result[2] = 3;});return result;});parentTask.Start();Console.WriteLine("Parent Task's Result is :");foreach (int result in parentTask.Result)Console.Write("{0}\t", result);Console.WriteLine();Console.WriteLine("Main Thread end!");Console.ReadKey(true);} }// 自定義的TaskScheduler,沒什么實際的作用,只是為了實驗自定義TaskScheduler public class MyTaskScheduler : TaskScheduler {private IList<Task> _lstTasks;public MyTaskScheduler(){_lstTasks = new List<Task>();}#region inherit from TaskSchedulerprotected override System.Collections.Generic.IEnumerable<Task> GetScheduledTasks(){return _lstTasks;}protected override void QueueTask(Task task){_lstTasks.Add(task);// 將原先的打印信息,移到此處統一處理Console.WriteLine("task " + task.Id + " is start!");TryExecuteTask(task);Console.WriteLine("task " + task.Id + " is end!");}protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued){return TryExecuteTask(task);}#endregion }

    ?

    5. 并行任務Parallel

    Parallel是為了簡化任務編程而新增的靜態類,利用Parallel可以將平時的循環操作都并行起來。

    下例演示了for并行循環,foreach并行循環與之類似。

    using System; using System.Threading.Tasks; using System.Threading; using System.Diagnostics;public class CLRviaCSharp_19 {static void Main(string[] args){Console.WriteLine("Main Thread start!");int max = 10;// 普通循環long start = Stopwatch.GetTimestamp();for (int i = 0; i < max; i++){Thread.Sleep(1000);}Console.WriteLine("{0:N0}", Stopwatch.GetTimestamp() - start);// 并行的循環start = Stopwatch.GetTimestamp();Parallel.For(0, max, i => { Thread.Sleep(1000); });Console.WriteLine("{0:N0}", Stopwatch.GetTimestamp() - start);Console.WriteLine("Main Thread end!");Console.ReadKey(true);} }

    在上面的例子中,采用并行循環消耗的時間不到原先的一半。

    但是,采用并行循環需要滿足一個條件,就是for循環中的內容能夠并行才行

    比如for循環中是個對 循環變量i 進行的累加操作(例如sum += i;),那就不能使用并行循環。

    ?

    還有一點需要注意,Parallel的方法本身有開銷

    所以如果for循環內的處理比較簡單的話,那么直接用for循環可能更快一些。

    比如將上例中的Thread.Sleep(1000);刪掉,再運行程序發現,直接for循環要快很多。

    轉載于:https://www.cnblogs.com/wang_yb/archive/2011/11/10/2244745.html

    與50位技術專家面對面20年技術見證,附贈技術全景圖

    總結

    以上是生活随笔為你收集整理的《CLR Via C# 第3版》笔记之(十九) - 任务(Task)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。