聊聊多線程那一些事兒 之 五 async.await深度剖析

来源:https://www.cnblogs.com/xiaoXuZhi/archive/2020/01/02/XYH_tsak_async_await.html
-Advertisement-
Play Games

前面已經講解了task的運行、阻塞、同步、延續操作、取消等!今天我們就專門來聊聊關於async/await的那一些事,分析其實現原理,通過該文章你也該對async的使用還有更加清晰的理解 ...


   hello task,咱們又見面啦!!是不是覺得很熟讀的開場白,哈哈你喲這感覺那就對了,說明你已經閱讀過了我總結的前面4篇關於task的文章,謝謝支持!感覺不熟悉的也沒有關係,在文章末尾我會列出前四篇文章的地址,可以點擊詳細閱讀。

    前幾篇文章分享了以後,無論是公眾號還是博客園,都有小伙伴問我async/await的專欄總結分享,既然這樣,那今天我們就專門來聊聊關於async/await的那一些事,通過該文章你也該對async的使用還有更加清晰的理解,謝謝!

async/await入門:

    async也就是我們說的非同步方法,不廢話,也不先說那麼多的大理論,先上一個簡單的實例,通過這個簡單的實例實現和asyncd 初相識!!

 

 static void Main(string[] args)
 {
     Console.WriteLine($"主線程開始,線程ID:{Thread.CurrentThread.ManagedThreadId}\n");

     // 同步實現 
     AddSync(1, 2);

     // 非同步方法,沒有 Await
     AddNoAwaitSyncHas(1, 2);

     // 非同步方法,有 Await
     AddHasAwaitAsync(1, 2);

     Console.WriteLine($"主線程結束,線程ID:{Thread.CurrentThread.ManagedThreadId}\n");
     Console.ReadLine();
     return;
 }

 /// <summary>
 /// 同步計算兩個數字之和
 /// </summary>
 /// <param name="num1">參數1</param>
 /// <param name="num2">參數2</param>
 /// <returns></returns>
 private static int AddSync(int num1, int num2)
 {
     Thread.Sleep(1000);
     Console.WriteLine($"同步方法,線程ID:{Thread.CurrentThread.ManagedThreadId}\n");
     return num1 + num2;
 }

 /// <summary>
 /// 對兩個數字求和 (非同步方法,沒有 Await)
 /// </summary>
 /// <param name="num1">參數1</param>
 /// <param name="num2">參數2</param>
 /// <returns>結果</returns>
 private static async Task<int> AddNoAwaitSyncHas(int num1, int num2)
 {
     Console.WriteLine($"非同步線程沒有await前,線程ID:{Thread.CurrentThread.ManagedThreadId}\n");
     // 兩個數字求和,假設其中會涉及到很耗時的邏輯
     Thread.Sleep(1000);
     Console.WriteLine($"非同步線程沒有await後,線程ID:{Thread.CurrentThread.ManagedThreadId}\n");
     return num1 + num2;
 }

 /// <summary>
 /// 對兩個數字求和 (非同步方法,有 Await)
 /// </summary>
 /// <param name="num1">參數1</param>
 /// <param name="num2">參數2</param>
 /// <returns>結果</returns>
 private static async Task<int> AddHasAwaitAsync(int num1, int num2)
 {
     Console.WriteLine($"非同步線程await前,線程ID:{Thread.CurrentThread.ManagedThreadId}\n");
     // 兩個數字求和,假設其中會涉及到很耗時的邏輯
     var add = Add(num1, num2);
     int result = await add;
     Console.WriteLine($"非同步線程await後,線程ID:{Thread.CurrentThread.ManagedThreadId}\n");
     return result;
 }

 /// <summary>
 /// Task 對兩個數字求和
 /// </summary>
 /// <param name="num1">參數1</param>
 /// <param name="num2">參數2</param>
 /// <returns>結果</returns>
 private static Task<int> Add(int num1, int num2)
 {
     // 假設該邏輯執行起來很耗時
     var task = Task.Run(() =>
     {
         Console.WriteLine($"我是Task內部執行開始:線程ID :{Thread.CurrentThread.ManagedThreadId}\n");
         Thread.Sleep(5000);
         Console.WriteLine($"我是Task內部執行結束:線程ID :{Thread.CurrentThread.ManagedThreadId}\n");
         return num1 + num2;
     });

     return task;
 }

執行結果:

 

結合代碼和執行結果,我們分析可以得出以下一些結論:

   1、通過async的寫法和同步方法在實現和調用上都很相似

   2、非同步方法async如果沒有await關鍵詞,其整體執行都是在主線中運行

    ----同步調用

    3、非同步方法async有await關鍵詞,其線程執行分水嶺就在await

     ----await前,async執行還是在主線中執行

     ----await後,async的執行邏輯會新開一個線程

     ----也就是說,async其真正的非同步還是await實現

     ​----而await修飾的實際是一個task修飾的變數或者返回的類型為task的方法體

     ​----所以最後的最後,async的非同步還是通過task來實現的

    4、await是不能單獨使用,一定是在是和async成對使用

     ----當然aysnc修飾的方法可以沒有await關鍵詞

  通過上面的一個簡單實例,是不是發現要實現一個非同步方法,是不是so easy,是的 ,你沒說錯,就是那麼簡單,但是也許你會問,幹嘛實現一個非同步方法整的的如此複雜,創建了這麼多方法,是的,不急不急,我這樣寫,是為了更加清晰的明白其執行流程。好了,下麵我們在一起來探討一下aysnc/await的組成結構吧!

 

aysnc/await的組成結構:

 

其實非同步方法的整體結構和一個普通的方法沒有多大區別,唯一不一樣的點,就是多了一個task邏輯主體,下麵簡單的分別來概要說明一下每一個環節:

    上面的圖簡單的繪製了一個非同步方法在整體執行時的一個執行順序。

非同步方法調用

    個人覺得這個沒有什麼說的,其實很普通方法調用一樣,只是說非同步方法的調用結果一般為一個Task對象,那麼需要獲獲取其執行結果的值,或者對執行結果需要做一些邏輯處理,這個和操作一個普通的task一樣,這兒就不在細說,不清楚的可以看我前面分享的幾篇文章,會有詳細的說明,謝謝!

aysnc的方法體

    通過實例我們應該已經知道,其實非同步方法,也就是在普通的方法體上,加了一個async修飾罷了,其簡單的結構大概是

    private aysnc task MyAysnc(){具體方法實現}

    說說aysnc的返回類型

    其返回類型有三種情況,每一種情況適用於不同的業務場景,如下:

    A、Tsak:其主要適用場景是,主程式只關心非同步方法執行狀態,不需要和主線程有任何執行結果數據交互。

    B、Task<T>:其主要適用場景是,主程式不僅僅關心非同步方法執行狀態,並且還希望執行後返回一個數據類型為T的結果

    C、void: 主程式既不關係非同步方法執行狀態,也不關心其執行結果,只是主程式調用一次非同步方法,對於除事件處理程式以外的代碼,通常不鼓勵使用 async void 方法,因為調用方不能

task邏輯主體

    aysnc為了實現非同步,其中最關鍵的一個點就是await修飾符,await修飾的也就是task實現邏輯主體。task實現邏輯主體,其實在上就是一個task實例,所以其裡面的實例邏輯使用和一個普通的task實例定義操作都是一樣的,在此也就不在詳細說明,前面的幾篇文章也有詳細的說明瞭,如果不清楚的可以查看以前的幾篇文章。

     

aysnc/await的原理分析:

 

    在說這一塊之前,我們先把寫的代碼編譯後,在通過反編譯後發現在代碼裡面根本找不到aysnc/await關鍵詞,有興趣的小伙伴,你也可以這樣操作分析一下。那麼我們就明白了aysnc/await其實是編譯器層面給的一個語法糖,是為了方便實現一個非同步方罷了。

從反編譯後的代碼看出編譯器新生成一個繼承IAsyncStateMachine 的狀態機結構asyncd(代碼中叫<AddHasAwaitAsync>d__2),下麵是基於反編譯後的代碼來分析的。

IAsyncStateMachine最基本的狀態機介面定義:

public interface IAsyncStateMachine {       
 void MoveNext();       
 void SetStateMachine(IAsyncStateMachine stateMachine); 
}

 

    好了,說道這兒我們已經知道aysnc/await是編程器層面的一個語法糖,那麼我們在來分析一下其執行的流程如下:

    第一步:主線程調用 AddHasAwaitAsync(1,2)非同步方法

   第二步:AddHasAwaitAsync()方法內初始化狀態機狀態為-1,啟動<AddHasAwaitAsync>d__2

    第三步:MoveNext方法內部開始執行,task.run實現了把業務邏輯執行丟到線程池中,返回一個可等待的任務句柄。其底層還是藉助委托實現。

    第四步:到此程式以及開啟了兩個線程,一個主線程,一個task線程,兩個線程相互獨立互不阻塞,各自執行對應的業務邏輯。

    好了,時間不早了,就先到這兒吧,感覺這一篇文章總結的不怎麼好,先這樣,後續我們在持續交流,謝謝!

 

猜您喜歡: 

 第一篇:聊聊多線程哪一些事兒(task)之 一創建運行與阻塞

 第二篇:聊聊多線程哪一些事兒(task)之 二 延續操作

 第三篇:聊聊多線程那一些事兒(task)之 三 非同步取消和非同步方法

 第四篇:聊聊多線程那一些事兒 之 四 經典應用(取與舍、動態創建)

END
為了更高的交流,歡迎大家關註我的公眾號,掃描下麵二維碼即可關註,謝謝:


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

-Advertisement-
Play Games
更多相關文章
  • 新手應該怎樣使用netty?如果將http服務(不含頁面)改造為使用socket的服務? ...
  • 簡介 一個國人編寫的強大的網路爬蟲系統並帶有強大的WebUI 採用Python語言編寫,分散式架構,支持多種資料庫後端,強大的WebUI支持腳本編輯器,任務監視器,項目管理器以及結果查看器 官方文檔:http://docs.pyspider.org/en/latest/ 安裝 pip install ...
  • 本書介紹瞭如何利用Python 3開髮網絡爬蟲,書中首先介紹了環境配置和基礎知識,然後討論了urllib、requests、正則表達式、Beautiful Soup、XPath、pyquery、數據存儲、Ajax數據爬取等內容,接著通過多個案例介紹了不同場景下如何實現數據爬取,*後介紹了pyspid... ...
  • 事務一般是指資料庫事務,是指作為一個程式執行單元執行的一系列操作,要麼完全執行,要麼完全不執行。事務就是判斷以結果為導向的標準。 一.spring的特性(ACID) (1).原子性(atomicity) 原子性就是一個不可分割的工作單元。簡單的說,就是指事務包含的所有操作要麼全部成功,要麼全部失敗回 ...
  • 背景 上文JDK8中的HashMap源碼寫了HashMap,這次寫ConcurrentHashMap ConcurrentHashMap源碼 /** * Maps the specified key to the specified value in this table. * Neither th ...
  • 背景 很久以前看過源碼,但是猛一看總感覺挺難的,很少看下去。當時總感覺是水平不到。工作中也遇到一些想看源碼的地方,但是遇到寫的複雜些的心裡就打退堂鼓了。 最近在接手同事的代碼時,有一些很長的python腳本,沒有一行註釋。就硬著頭皮一行一行的讀,把理解的都加上註釋,這樣一行行看下來,終於知道代碼的意 ...
  • 新電腦clone項目後發現Project Interpreter無法配置, New environment 選擇後無法應用, 滑鼠懸停在Location 提示 Environment location directory is not empty . 原因是項目push時, 項目下的venv文件夾也 ...
  • crm業務的流程圖,都是比較精簡的內容,後面有機會的話會繼續擴展 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...