異步編程系列第02章 你有什麼理由使用Async異步編程

寫在前面

  在學異步,有位園友推薦了《async in C#5.0》,沒找到中文版,恰巧也想提升下英文,用我拙劣的英文翻譯一些重要的部分,純屬娛樂,簡單分享,保持學習,謹記謙虛。html

  若是你以爲這件事兒沒意義翻譯的又差,盡情的踩吧。若是你以爲值得鼓勵,感謝留下你的贊,願愛技術的園友們在從此每一次應該猛烈突破的時候,不選擇知難而退。在每一次應該獨立思考的時候,不選擇隨波逐流,應該盡心盡力的時候,不選擇盡力而爲,不辜負每一秒存在的意義。node

   轉載和爬蟲請註明原文連接http://www.cnblogs.com/tdws/p/5618321.html,博客園 蝸牛 2016年6月25日6:15pm。web

目錄

第01章 異步編程介紹數據庫

第02章 爲何使用異步編程編程

    桌面應用程序
    打個比方:咖啡館
    Web應用程序服務端代碼
    另外一個比方:餐館廚房
    Silverlight, Windows Phone, and Windows 8
    並行代碼
    一個示例 服務器

第03章 手動編寫異步代碼網絡

第04章 編寫異步方法多線程

第05章 Await究竟作了什麼併發

第06章 以Task爲基礎的異步模式app

第07章 異步代碼的一些工具

第08章 哪一個線程在運行你的代碼

第09章 異步編程中的異常

第10章 並行使用異步編程

第11章 單元測試你的異步代碼

第12章 ASP.NET應用中的異步編程

第13章 WinRT應用中的異步編程

第14章 編譯器在底層爲你的異步作了什麼

第15章 異步代碼的性能

爲何使用異步編程

  異步編程很重要頗有用處,緣由有不少,主要看你在構建什麼類型的應用程序。其中一部分的用處和益處在任何應用程序中都隨處可見,即便某些類型的應用程序你沒有接觸過。若是這適合你,請閱讀整篇文章,作爲背景知識將會幫助你更好的理解整個上下文。

 
桌面應用程序

  桌面app有一個主要的性能要求。要求app讓用戶一直感受到是可響應的。HCI研究代表,緩慢的應用程序不會獲得用戶的關注,最好是要有一個進度條提示器。

  當應用程序凍住或者說未響應狀態,用戶會變得沮喪。凍結的緣由一般是由於一個耗時操做,或者由於一個緩慢的計算,或者由於IO,或者網絡請求。

  你用的C#的桌面UI框架都是單個UI線程的,包括:

·Winforms

·WPF

·Silverlight

  UI線程是惟一能夠控定特性窗口的,也是惟一一個線程來檢查用戶輸入和爲他們執行相應工做。若是這個線程很繁忙或者被阻塞數幾十毫秒,用戶就會注意到這個app是緩慢的。

  異步代碼,甚至手工編寫,意味着UI線程能夠返回檢查消息隊列進行對用戶操做的主要工做,並對他們響應。它也能夠執行進度動畫,並在最近版本的窗口,鼠標懸停動畫,這都是重要的視覺線索給用戶,給人一個好印象的響應的應用程序。

   全部常見的用戶界面框架只使用一個線程的緣由是爲了簡化同步。若是有不少線程的話,當一個線程正在鋪設佈局控件過程當中,另外一個線程試圖讀取按鈕的寬度,這樣衝突了。爲了不這樣的事情發生,你須要大量的使用鎖,這將會大大下降程序的性能。

 
打個比方:咖啡館

  我想用一個比喻來直觀的幫你把握所涉及的問題。若是你以爲已經理解,那麼請跳過這一小節。

  想象一下,有個小咖啡館爲顧客早餐提供麪包,惟一的工做人員就是老闆,他很是注重和關心客戶服務,可是尚未學會異步的技術。UI線程模型和咖啡店的老闆很類似,就像在計算機中必須經過線程同樣,咖啡店員工在咖啡館中工做,在這樣的狀況下,就像他們只有一個UI線程同樣。

  第一個客戶要一片面包,老闆將麪包放到烤箱中,而後他在烤麪包的過程當中等待着。客戶問老闆在哪能找到黃油,可是老闆把她忽略了,就像他是阻塞型代碼同樣。五分鐘過去,麪包已經烤好而且拿給了客戶。到了這個時候,已經排起了長隊,客戶最討厭的就是等待和被忽略,老闆這樣作一點也不理想。

   如今,咱們來教老闆異步。

  首先要確保他的烤箱能夠異步操做,當咱們編寫異步代碼時,咱們要確保耗時操做在執行結束時可以告訴咱們,一樣的,麪包烤箱也須要一個定時器,而且要響亮以便可以被他注意到。

  另外就是他可以在烤箱開始的時候忽略它,他應該回去繼續爲顧客服務,一樣的,咱們的異步代碼也要在執行耗時操作時返回,這樣UI線程能夠繼續相應用戶操做。

  有兩點緣由:

·能夠更好的響應用戶請求—客戶能夠詢問黃油在哪而且不被老闆忽略。

·用戶能夠同時開始另外一個操做—下一個客戶也能夠開始點餐而且讓老闆烹飪。

  咖啡館老闆如今能夠同時爲多個客戶服務,惟一的限制就是烤箱的數量和拿取麪包的時間。但這樣帶來了一系列問題:老闆發現本身並不能記住哪片面包是哪一個客戶所點,UI線程徹底不能記住當他返回時,他在等待的是哪一個操做。

  因此咱們必須再開始的時候附加一個回調,去提醒咱們當他結束時該作什麼。至於咖啡館老闆,很簡單的就是在烤箱上貼上客戶姓名的標籤。但咱們可能須要更復雜的東西,通常來講咱們但願可以對耗時工做結束後須要作什麼事情,提供完整說明,一旦完成了工做。

  有了這些,咖啡館老闆如今徹底是異步的了,而且生意紅火。用戶體驗感變得更好。這就是等待不多,更具效應能力。但願這個比方可以幫助你的直覺來理解爲何異步在UI application上如此重要。

 
Web應用程序服務端代碼

  ASP.NET的Web服務器沒有像UI代碼一個線程的的硬限制。這就是說,在web中使用多線程編程依然有不少好處。耗時操做,尤爲是遠程數據庫Query,在web app中很是常見。

  取決於你的IIS版本,將會有用於處理web請求的線程總數和併發請求數的限制。若是你的請求花了大量的時間在等待數據庫Query上,那麼增長併發請求數去增長服務器吞吐量是一個好的方法。

  當一個線程被阻塞,一直在等待,它不佔用UPU時間。然而,你不要誤覺得這意味着它不佔用服務器資源。事實上,線程佔用兩項重要的開銷,即便他們在被阻塞:

·內存

   每一個託管線程儲備在Windows上的虛擬內存的字節。若是你有幾十個線程是徹底沒有問題的,可是當你有上百個線程時很容易就會失控。若是內存使用了磁盤上的虛擬內存空間,那麼你的線程會變得特別慢。

·調度器的開銷

   操做系統的調度器負責選擇在什麼時間,在哪一個CPU上,執行哪一個線程。即便當線程被阻塞時,調度器也必須考慮到他們,而且判斷它們是否變得非阻塞(阻塞結束)。這樣減緩了線程上下文切換,甚至能夠拖慢整個系統。

  在他們之間,這些開銷負載到到你的服務器上,增長延遲而且下降吞吐量。

  請記住:異步編程的主要特徵是當線程開始執行一段耗時操做,這個線程會被釋放去作其餘一些事情。在ASP.NET代碼下,線程來自於線程池,因此在執行耗時操做期間,線程會被返回到線程池,他能夠處理其餘請求,以不多的線程就能夠來處理同阻塞代碼狀況相同的請求數量。//譯者註解,若是你不理解這句話,能夠參照個人另外一篇文章中的解惑二:文章連接

 
另外一個比方:餐館廚房

  web服務器和餐館模型很接近,不少客人點餐,廚房會盡量的快速知足他們的要求。

  咱們的廚房有不少廚師,每一個廚師表明一個線程。他們按照用戶的訂單來烹飪,可是在整個準備過程當中,每道菜只須要烹飪一下子,而且廚師可能等待着沒什麼事情作。這反映了web請求處理的方式,一般在執行數據庫Query請求數據庫數據的這段時間,web服務器並無參與。

  在相似於阻塞型的廚房,廚師將會在烹飪工具前等待菜餚的烹飪。精確模擬一個線程,廚師有一個奇怪的合同,廚師在等待烹飪結束的過程當中不被支付薪水,由於一個線程不佔用CPU時間當他們被阻塞時,也須要他們在這期間讀報紙。

  但即便咱們沒有向他們支付,咱們依然可使用新廚師爲另外一道菜餚,那些等待烹飪結束的廚師依然空閒在廚房。可是,咱們不能讓幾十個廚師在廚房工做,這樣連轉身都困難,最終致使每一個人的工做效率都很低。

  固然咱們用異步工做方式會更好,每當菜餚正在烤箱中烹飪,廚師記下當前在烹飪的是什麼菜餚,在什麼階段或者階段,而後找到一項新的任務(菜餚)去作。當菜餚烹飪結束,任意一個廚師能夠將菜餚拿出來繼續處理。//譯者註釋:記住,每一個廚師表明一個線程,試着看成線程來理解,你會明白原理。

   Web服務器正是這種強大的系統。是須要極少部分線程就能夠承受之前的併發量,或者能作到之前在開銷上不可行的事情。事實上,一些web框架,特別是nodejs,拒絕多線程並行的方式,選擇單個線程來異步處理全部請求。他們能夠用單線程來處理比多線程並行更多的請求,可是阻塞,系統能夠處理的總數。一樣的,一個組織能力強的廚師在一個空廚房中能夠烹飪比,上百個廚師在廚房還多的菜餚,由於出事多了他們花了幾乎全部時間在絆倒彼此和閱讀報紙上。

 
Silverlight, Windows Phone, and Windows 8

  暫時不翻譯此部分

 
並行代碼

  計算機是多處理核心的,每一個核心之間相互獨立。程序須要充分利用多核心的優點,但由這些程序使用的任何內存不能被並行代碼當即同時寫入,不然內存容易被損害。

   也許在編程中使用比較純淨的統一的風格是更好的,即不在內存中操做狀態,而是操做不變值。這將會讓咱們享受並行系統的好處,但這也不適用於某些程序。就像User Interfaces須要狀態,數據庫就是狀態。//譯者註釋:原文的狀態是state,這個詞也許翻譯爲狀態不恰當。

  標準的解決方案是使用互斥鎖以防並行代碼同時訪問相同內存。但這又帶來一系列問題,你的代碼一般會帶一把鎖,而後作出一個方法調用或註冊一個事件又帶另外一個鎖。一般,同時保持兩個鎖不是必要的,但代碼是沒有人類這樣思想的。

  下面是一個對於鎖的假設結構,意思是說,整體來說,更多的線程結束,等待鎖,直到他們能夠作一些有用的工做。在一些狀況下,兩個線程同時等待另外一個線程保持的鎖,引起了死鎖。這些錯誤是很難預測,很難重現,並且每每很難修復。

  最有前景的解決方案之一是Actor模型計算。這是一個在每塊可寫的內存只能存在於一個Actor內的設計。惟一的方式來使用這塊內存是向Actor發送消息,從而一次處理一個,而且可能會獲得另外一條回覆消息。這就是異步編程。詢問Actor的操做是一種典型的異步操做,由於咱們能夠繼續作其餘事情直到回覆消息抵達。這意味着你可使用異步來作,詳細將會見第10章.

 
一個示例

  咱們將會看到一個desktop UI app,它急需轉換爲異步風格。源代碼地址https://bitbucket.org/alexdavies74/faviconbrowser .我建議你若是能夠的話跟隨着來進行,在VS中打開。

  運行程序,你會看到一個窗口有一個按鈕。若是你按這個按鈕,它會顯示一些流行的網站的圖標。它經過下載大多數網站包含的一個文件名爲favicon.ico(圖2-1)。

************************配圖 downloads the favicon and adds it to a WPF WrapPanel in the window.

   讓咱們看一看代碼。重要的方法是下載的圖標,並將其添加到窗口中的WPF wrappanel。

private void AddAFavicon(string domain)
{
WebClient webClient = new WebClient();
byte[] bytes = webClient.DownloadData("http://" + domain + "/favicon.ico");
Image imageControl = MakeImageControl(bytes);
m_WrapPanel.Children.Add(imageControl);
}

  你將會注意到代碼但全是同步的,線程在下載的過程當中是阻塞的。你可能還會注意到點擊按鈕的時候,窗口有幾秒變成了未響應的狀態。和你知道的同樣,這是由於在下載小圖標icons時UI線程阻塞,而且不能返回處理用戶的操做。

  咱們將使用這個例子在接下來的章節,並將其轉化爲異步編程的程序。

寫在最後

  早上九點多開始,除了吃飯,健身,基本一直在翻譯和學習第二章。我以爲對於ASP.NET異步編程,廚房這個這個比方簡直太棒太恰當了!若是你對你有些許益處,不要吝嗇你的贊,給個鼓勵。不許確的地方,也請前輩們不吝賜教,我將虛心改正。

下一章 http://www.cnblogs.com/tdws/p/5628538.html

相關文章
相關標籤/搜索