線程也瘋狂-----非同步編程

来源:http://www.cnblogs.com/dongqinnanren/archive/2017/02/04/6278964.html
-Advertisement-
Play Games

前言 本節主要介紹非同步編程中Task、Async和Await的基礎知識。 什麼是非同步? 非同步處理不用阻塞當前線程來等待處理完成,而是允許後續操作,直至其它線程將處理完成,並回調通知此線程。 非同步和多線程 相同點:避免調用線程阻塞,從而提高軟體的可響應性。 不同點: 非同步操作無須額外的線程負擔,並且使 ...


前言

本節主要介紹非同步編程中Task、Async和Await的基礎知識。

什麼是非同步?

非同步處理不用阻塞當前線程來等待處理完成,而是允許後續操作,直至其它線程將處理完成,並回調通知此線程。

非同步和多線程

相同點:避免調用線程阻塞,從而提高軟體的可響應性。

不同點:

非同步操作無須額外的線程負擔,並且使用回調的方式進行處理,在設計良好的情況下,處理函數可以不必使用共用變數(即使無法完全不用,最起碼可以減少 共用變數的數量),減少了死鎖的可能。C#5.0 .NET4.5 以後關鍵字Async和Await的使用,使得非同步編程變得異常簡單。

多線程中的處理程式依然是順序執行,但是多線程的缺點也同樣明顯,線程的使用(濫用)會給系統帶來上下文切換的額外負擔。並且線程間的共用變數可能造成死鎖的出現。

非同步應用場景及原理

非同步主要應用於IO操作,資料庫訪問,磁碟操作,Socket訪問、HTTP/TCP網路通信 

原因:對於IO操作並不需要CPU進行過多的計算,這些數據主要通過磁碟進行處理,如果進行同步通信無法結束,需要創建更多的線程資源,線程的數據上下文頻繁的切換也是對資源的浪費,針對IO操作不需要單獨的分配一個線程來處理。

舉例說明:

操作:伺服器接收HTTP請求對資料庫進行操作然後返回

同步處理請求的線程會被阻塞,非同步處理請求的線程不會阻塞。

 

任務

  在使用任務之前,針對線程的調用大多都用線程池提供的靜態方法QueueUserWorkItem,但是這個函數有很多的限制,其中最大的問題就是沒有內部機制可以讓開發者知道操作在什麼時候完成,也沒有機制在操作完成時獲取返回值,微軟為瞭解決這個問題引入了任務的概念。

 首先構造一個Task<TResult>對象,併為TResult傳遞返回值,開始任務之後等待它並回去結果,示例代碼:

 1 static void Main(string[] args)
 2         {
 3             Console.WriteLine("開始進行計算");
 4            // ThreadPool.QueueUserWorkItem(Sum, 10);
 5             Task<int> task = new Task<int>(Sum, 100);
 6             task.Start();
 7             //顯示等待獲取結果
 8             task.Wait();
 9             //調用Result時,等待返回結果
10             Console.WriteLine("程式結果為 Sum = {0}",task.Result);
11             Console.WriteLine("程式結束");
12             Console.ReadLine();
13         }
14 
15         public static int Sum(object i)
16         {
17             var sum = 0;
18             for (var j = 0; j <= (int) i; j++)
19             {
20                 Console.Write("{0} + ",sum);
21                 sum += j;
22             }
23             Console.WriteLine( " = {0}",sum);
24             return sum;
25         }

除了wait等待單個任務外,task還提供了等待多個任務,WaitAny和WaitAll,它阻止調用線程,直到數組中所有的Task對象完成。

取消任務

任務的取消同樣使用的是.NET Framework的標準取消操作模式,首先需要創建一個CancellationTokenSource對象,然後在函數中加入參數CancellationToken,將CancellationTokenSource的Token傳遞給方法,然後調用IsCancellationRequested獲取是否已經取消該值進行判斷。

 1 static void Main(string[] args)
 2         {
 3             Console.WriteLine("開始進行計算");
 4             // ThreadPool.QueueUserWorkItem(Sum, 10);
 5             var  ctx = new CancellationTokenSource();
 6             var task = new Task<int>(() => Sum(ctx.Token, 100000));
 7             task.Start();
 8             //顯示等待獲取結果
 9             //task.Wait(ctx.Token);
10             Thread.Sleep(1000);
11             ctx.Cancel();
12             //調用Result時,等待返回結果
13             Console.WriteLine("程式結果為 Sum = {0}", task.Result);
14             Console.WriteLine("程式結束");
15             Console.ReadLine();
16         }
17 
18         public static int Sum(CancellationToken cts, object i)
19         {
20             var sum = 0;        
21             for (var j = 0; j <= (int)i; j++)
22             {
23                 if (cts.IsCancellationRequested) return sum;
24                 Thread.Sleep(50);
25                 Console.Write("{0} + ", sum);
26                 sum += j;
27             }
28             Console.WriteLine(" = {0}", sum);
29             return sum;
30         }

任務完成後自動啟動新任務

實際的開發應用中,經常出現一次任務完成後立刻啟動另外一個任務,並且不能夠使線程阻塞,在任務尚未完成時調用result會使程式阻塞,無法查看任務的執行進度,TASK提供了一個方法ContinueWith,它不會阻塞任何線程,當第一個任務完成時,會立即啟動第二個任務。

 1 static void Main(string[] args)
 2         {
 3             Console.WriteLine("開始進行計算");
 4             // ThreadPool.QueueUserWorkItem(Sum, 10);
 5             var  ctx = new CancellationTokenSource();
 6             var task = new Task<int>(() => Sum(ctx.Token, 100000));
 7             task.Start();
 8             var cwt = task.ContinueWith(p =>
 9             {
10                 Console.WriteLine("task result ={0} ",task.Result);
11             });
12             //顯示等待獲取結果
13             //task.Wait(ctx.Token);
14             Thread.Sleep(1000);
15             ctx.Cancel();
16             //調用Result時,等待返回結果
17             Console.WriteLine("程式結果為 Sum = {0}", task.Result);
18             Console.WriteLine("程式結束");
19             Console.ReadLine();
20         }
21 
22         public static int Sum(CancellationToken cts, object i)
23         {
24             var sum = 0;        
25             for (var j = 0; j <= (int)i; j++)
26             {
27                 if (cts.IsCancellationRequested) return sum;
28                 Thread.Sleep(50);
29                 Console.Write("{0} + ", sum);
30                 sum += j;
31             }
32             Console.WriteLine(" = {0}", sum);
33             return sum;
34         }

Async&Await 簡單使用

使用Async&Await的主要目的是方便進行非同步操作,因為.net 4.0 以前進行非同步操作時比較複雜的,主要是通過調用微軟提供的非同步回調方法進行編程,如果遇到需要自己實現的方法顯得非常頭疼,.net的各個版本都有自己主推的技術,像.NET1.1中的委托,.NET2.0中的泛型,.NET3.0中的Linq,.NET4.0中的Dynimac,.net4.5主推的就是非同步編程,大家只需要瞭解TASK+非同步函數就可以實現非同步編程。

async:告訴CLR這是一個非同步函數。

await:  將Task<TResult>返回值的函數進行非同步處理。

 

示例目的:獲取網址JS代碼,併在界面顯示。

 1  private static async Task<string> DownloadStringWithRetries(string uri)
 2         {
 3             using (var client = new HttpClient())
 4             {
 5                 // 第1 次重試前等1 秒,第2 次等2 秒,第3 次等4 秒。
 6                 var nextDelay = TimeSpan.FromSeconds(1);
 7                 for (int i = 0; i != 3; ++i)
 8                 {
 9                     try
10                     {
11                         return await client.GetStringAsync(uri);
12                     }
13                     catch
14                     {
15                     }
16                     await Task.Delay(nextDelay);
17                     nextDelay = nextDelay + nextDelay;
18                 }
19                 // 最後重試一次,以便讓調用者知道出錯信息。
20                 return await client.GetStringAsync(uri);
21             }
22         }
 1  static  void Main(string[] args)
 2         {
 3             Console.WriteLine("獲取百度數據");
 4             ExecuteAsync();
 5             Console.WriteLine("線程結束");
 6             Console.ReadLine();
 7         }
 8 
 9         public static async void ExecuteAsync()
10         {
11            string text = await DownloadStringWithRetries("http://wwww.baidu.com");
12            Console.WriteLine(text);
13         }

運行結果發現,首先獲取百度數據,線程結束,最後顯示HTML代碼,這是因為非同步開啟了新的線程,並不會造成線程阻塞。

 
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在實現API Gateway過程中,另外一個需要考慮的問題就是部分失敗。這個問題發生在分散式系統中當一個服務調用另外一個服務超時或者不可用的情況。API Gateway不應該被阻斷並處於無限期等待下游服務的狀態。但是,如何處理這種失敗依賴於特定的場景和具體服務。如果是產品信息服務無響應,那麼API ...
  • 前言 MoonSharp是一個支持C#調用lua腳本的類庫,支持.net, .net core, mono, unity,因此在.net core中也能夠使用,而且載入和調用lua也很方便簡單; 官網:http://www.moonsharp.org/ 源碼:https://github.com/x ...
  • 前言 在工作中經常遇到C#數組、ArrayList、List、Dictionary存取數據,但是該選擇哪種類型進行存儲數據,對於初學者的我一直不知道該怎麼取捨。於是抽空好好看了下他們的用法和比較,在這裡總結下來,後面有需要改進的再更新。 初始化 數組: ArrayList: List: Dictio ...
  • "文章原link" 場景載入器 人員 | 操作 | 日期 | 備註 | | | Conerlius | Add | 2017 2 5 | 創建 scene的進場順序,方便對每個場景的回退,同一個彈出式的視窗在游戲中只允許出現一個,彈出視窗的進場順序管理 基本類型說明: Manager解釋 對ui的堆 ...
  • 創建Visual Studio插件,項目模板,項目創建嚮導等原來也是如此簡單。本文通過開發Prism的Plugin實例講解其中的實現過程與註意事項。 ...
  • using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Data;using System.Data.SqlClient;using System.Data.Commo ...
  • Xamarin寫Android程式時,通常要使用按中文首字母分組顯示(如通訊錄) 。 於是需要被迫包含CJK,不過包含後包肯定是會變大的,於是。。。。自己寫了一個硬枚舉的中文轉拼音的類。 原理是這樣的: 源碼: https://github.com/chsword/PinYinUtil/blob/m ...
  • 在本節中,您將創建一個新的MoviesController類,併在這個Controller類里編寫代碼來取得電影數據,並使用視圖模板將數據展示在瀏覽器里。 在開始下一步前,先Build一下應用程式(生成應用程式)(確保應用程式編譯沒有問題) 用滑鼠右鍵單擊Controller文件夾,並創建一個新的  ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...