寫在前面
在學異步,有位園友推薦了《async in C#5.0》,沒找到中文版,恰巧也想提升下英文,用我拙劣的英文翻譯一些重要的部分,純屬娛樂,簡單分享,保持學習,謹記謙虛。html
若是你以爲這件事兒沒意義翻譯的又差,盡情的踩吧。若是你以爲值得鼓勵,感謝留下你的贊,願愛技術的園友們在從此每一次應該猛烈突破的時候,不選擇知難而退。在每一次應該獨立思考的時候,不選擇隨波逐流,應該盡心盡力的時候,不選擇盡力而爲,不辜負每一秒存在的意義。node
轉載和爬蟲請註明原文連接http://www.cnblogs.com/tdws/p/5618321.html,博客園 蝸牛 2016年6月25日6:15pm。web
目錄
爲何使用異步編程
異步編程很重要頗有用處,緣由有不少,主要看你在構建什麼類型的應用程序。其中一部分的用處和益處在任何應用程序中都隨處可見,即便某些類型的應用程序你沒有接觸過。若是這適合你,請閱讀整篇文章,作爲背景知識將會幫助你更好的理解整個上下文。
桌面app有一個主要的性能要求。要求app讓用戶一直感受到是可響應的。HCI研究代表,緩慢的應用程序不會獲得用戶的關注,最好是要有一個進度條提示器。
當應用程序凍住或者說未響應狀態,用戶會變得沮喪。凍結的緣由一般是由於一個耗時操做,或者由於一個緩慢的計算,或者由於IO,或者網絡請求。
你用的C#的桌面UI框架都是單個UI線程的,包括:
·Winforms
·WPF
·Silverlight
UI線程是惟一能夠控定特性窗口的,也是惟一一個線程來檢查用戶輸入和爲他們執行相應工做。若是這個線程很繁忙或者被阻塞數幾十毫秒,用戶就會注意到這個app是緩慢的。
異步代碼,甚至手工編寫,意味着UI線程能夠返回檢查消息隊列進行對用戶操做的主要工做,並對他們響應。它也能夠執行進度動畫,並在最近版本的窗口,鼠標懸停動畫,這都是重要的視覺線索給用戶,給人一個好印象的響應的應用程序。
全部常見的用戶界面框架只使用一個線程的緣由是爲了簡化同步。若是有不少線程的話,當一個線程正在鋪設佈局控件過程當中,另外一個線程試圖讀取按鈕的寬度,這樣衝突了。爲了不這樣的事情發生,你須要大量的使用鎖,這將會大大下降程序的性能。
我想用一個比喻來直觀的幫你把握所涉及的問題。若是你以爲已經理解,那麼請跳過這一小節。
想象一下,有個小咖啡館爲顧客早餐提供麪包,惟一的工做人員就是老闆,他很是注重和關心客戶服務,可是尚未學會異步的技術。UI線程模型和咖啡店的老闆很類似,就像在計算機中必須經過線程同樣,咖啡店員工在咖啡館中工做,在這樣的狀況下,就像他們只有一個UI線程同樣。
第一個客戶要一片面包,老闆將麪包放到烤箱中,而後他在烤麪包的過程當中等待着。客戶問老闆在哪能找到黃油,可是老闆把她忽略了,就像他是阻塞型代碼同樣。五分鐘過去,麪包已經烤好而且拿給了客戶。到了這個時候,已經排起了長隊,客戶最討厭的就是等待和被忽略,老闆這樣作一點也不理想。
如今,咱們來教老闆異步。
首先要確保他的烤箱能夠異步操做,當咱們編寫異步代碼時,咱們要確保耗時操做在執行結束時可以告訴咱們,一樣的,麪包烤箱也須要一個定時器,而且要響亮以便可以被他注意到。
另外就是他可以在烤箱開始的時候忽略它,他應該回去繼續爲顧客服務,一樣的,咱們的異步代碼也要在執行耗時操作時返回,這樣UI線程能夠繼續相應用戶操做。
有兩點緣由:
·能夠更好的響應用戶請求—客戶能夠詢問黃油在哪而且不被老闆忽略。
·用戶能夠同時開始另外一個操做—下一個客戶也能夠開始點餐而且讓老闆烹飪。
咖啡館老闆如今能夠同時爲多個客戶服務,惟一的限制就是烤箱的數量和拿取麪包的時間。但這樣帶來了一系列問題:老闆發現本身並不能記住哪片面包是哪一個客戶所點,UI線程徹底不能記住當他返回時,他在等待的是哪一個操做。
因此咱們必須再開始的時候附加一個回調,去提醒咱們當他結束時該作什麼。至於咖啡館老闆,很簡單的就是在烤箱上貼上客戶姓名的標籤。但咱們可能須要更復雜的東西,通常來講咱們但願可以對耗時工做結束後須要作什麼事情,提供完整說明,一旦完成了工做。
有了這些,咖啡館老闆如今徹底是異步的了,而且生意紅火。用戶體驗感變得更好。這就是等待不多,更具效應能力。但願這個比方可以幫助你的直覺來理解爲何異步在UI application上如此重要。
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