1.基本概念
多執行緒與異步是兩個不同概念,之所以把這兩個放在一起學習,是因為這兩者雖然有區別,但也有一定聯系,
多執行緒是一個技術概念,相對于單執行緒而言,多執行緒是多個單執行緒同時處理邏輯,例如,假如說一個人把水從A地提到B點可看作是單執行緒,那么如果兩個人同時去做事(可以是相同的一件事,也可以是不同的一件事)就可以看作是兩個執行緒,
異步:記得讀書時學過一篇課文叫《統籌方法》,里面講述煮茶喝的程序,如下:
比如,想泡壺茶喝,當時的情況是:開水沒有;水壺要洗,茶壺、茶杯要洗;火已生了,茶葉也有了,怎么辦?
辦法甲:洗好水壺,灌上涼水,放在火上;在等待水開的時間里,洗茶壺、洗茶杯、拿茶葉;等水開了,泡茶喝,
辦法乙:先做好一些準備作業,洗水壺,洗茶壺茶杯,拿茶葉;一切就緒,灌水燒水;坐待水開了,泡茶喝,
那么辦法甲就是異步方法,辦法乙就是同步方法,利用多執行緒就可以實作辦法甲:如用A執行緒做“洗好水壺,灌上涼水,放在火上”,另B執行緒做“洗茶壺、洗茶杯、拿茶葉”,并在B執行緒中等待A執行緒執行完畢(即水開),再繼續做“泡茶喝”,
上面是示例講法,所謂的“等待”翻譯成技術語言就是“阻塞”,異步與同步的區別就是看一事件中是否有阻塞,等待另一件事情的處理結果,從示例中也可以看到,異步中用到了多執行緒,但異步和多執行緒顯然是兩個不同的概念,
結論:異步是相對同步來說的一個目的,而多執行緒是實作這個目的一種技術方法,
2.Task的運用
Task是.NET Framework 4.5加入的概念,之前實作多執行緒是利用Thread類,在此只對Task進行學習,在實際編碼中基本用Task,因為它比Thread更易理解,更易運用,更安全可靠,
下面是兩者差異的一個總結:
1.task與thread對比,task相當于應用層,thread更底層,但二者是不一樣的,沒有隸屬關系
2.task是在執行緒池上創建,是后臺執行緒(主執行緒不會等其完成);Thread是單個執行緒,默認是前臺執行緒
3.task可以直接獲取回傳值,thread不能直接從方法回傳結果(可以使用變數來獲取結果)
4.使用task.ContinueWith()方法可以繼續執行其他任務,執行緒中無連續性,當執行緒完成作業時,不能告訴執行緒開始其他操作, 盡管可以使用Join()等待執行緒完成,但是這會阻塞主執行緒
5.task借助CancellationTokeSource類可以支持任務中的取消,當thread處于運行中時,無法取消它
6.task能方便捕捉到運行中的例外,thread在父方法中無法捕捉到例外
下面用示例代碼來展示Task實作異步的基本運用:
public static void Main() { // Start the HandleFile method. Task<int> task = HandleFileAsync();//可以看作是一個耗時任務 // Control returns here before HandleFileAsync returns. // ... Prompt the user. Console.WriteLine("Please wait patiently " + "while I do something important."); // Do something at the same time as the file is being read. string line = Console.ReadLine(); Console.WriteLine("You entered (asynchronous logic): " + line); // Wait for the HandleFile task to complete. // ... Display its results. task.Wait();//有呼叫.result時,這里可以省略,在沒有呼叫.result時,一定要.wait()下,這個話題涉及到當task運行出現例外時:為什么要呼叫Wait或者Result?
或者一直不查詢Task的Exception屬性?你的代碼就永遠注意不到這個例外的發生,如果不能捕捉到這個例外,垃圾回收時,
拋出AggregateException,行程就會立即終止,這就是“牽一發動全身”,莫名其妙程式就自己關掉了 var x = task.Result;//其實在用Result的時候,內部會呼叫Wait Console.WriteLine("Count: " + x); Console.WriteLine("[DONE]"); Console.ReadLine(); } static async Task<int> HandleFileAsync()//async與下文的await是成對出現,否則不能實作真正的異步,而是同步執行, { string file = @"C:\enable1.txt";//在這個檔案中輸入幾個字符和輸入1MB字符,最終輸出結果會有不同,能很好的展示異步呼叫方式 Console.WriteLine("HandleFile enter"); int count = 0; // Read in the specified file. // ... Use async StreamReader method. using (StreamReader reader = new StreamReader(file)) { string v = await reader.ReadToEndAsync(); //string v = reader.ReadToEnd(); // ... Process the file data somehow. count += v.Length; // ... A slow-running computation. // Dummy code. for (int i = 0; i < 10000; i++) { int x = v.GetHashCode(); if (x == 0) { count--; } } } Console.WriteLine("HandleFile exit"); return count; }
上面是task的基本運用,看看注釋了解一些注意事項,下面是對ContinueWith,即連續任務的一個運用,如下:
static void Main() { // Call async method 10 times. for (int i = 0; i < 10; i++) { Run2Methods(i); } // The calls are all asynchronous, so they can end at any time. Console.ReadLine(); } static async void Run2Methods(int count) { // Run a Task that calls a method, then calls another method with ContinueWith. int result = await Task.Run(() => GetSum(count)) .ContinueWith(task => MultiplyNegative1(task)); Console.WriteLine("Run2Methods result: " + result); } static int GetSum(int count) { // This method is called first, and returns an int. int sum = 0; for (int z = 0; z < count; z++) { sum += (int)Math.Pow(z, 2); } return sum; } static int MultiplyNegative1(Task<int> task) { // This method is called second, and returns a negative int. return task.Result * -1; }
下面是如果想取消任務時CancellationTokenSource類的運用:
static void Main(string[] args) { using (var cts = new CancellationTokenSource()) { Task task = new Task(() => { LongRunningTask(cts.Token); }); task.Start(); Console.WriteLine("Operation Performing..."); if (Console.ReadKey().Key == ConsoleKey.C) { Console.WriteLine("Cancelling.."); cts.Cancel(); } Console.Read(); } } private static void LongRunningTask(CancellationToken token) { for (int i = 0; i < 10000000; i++) { if (token.IsCancellationRequested) { break; } else { Console.WriteLine(i); } } }
以上是Task的基本運用,
3.總結
以上是利用Task、async、await實作異步的方法,示例三展示的是task實作任務的取消,這里不是一個異步方法,只是一個單純的任務取消(可理解為多執行緒的取消),Task本質是類似于threadPool的一個執行緒池,只需把要做的事丟進去,底層執行緒怎么分配怎么運行,編碼時完全不用關心,如果是用thread類,就需要自己去控制執行緒的啟停銷毀等,控制不好就會“翻車”,而且這種“翻車”會導致出現莫名其妙的結果,甚至程式鎖死,因此在實際編碼中盡量使用task類,
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/114232.html
標籤:C#
上一篇:C# extract multiples from web pages based on OpenQA.Selenium.Chrome and ChromeDriver
