2016.10.11補充 三個月過去了,回頭來看,我不得不承認這是一系列失敗的翻譯。過段時間,我將重新翻譯。
在學異步,有位園友推薦了《async in C#5.0》,沒找到中文版,恰巧也想提高下英文,用我拙劣的英文翻譯一些重要的部分,純屬娛樂,簡單分享,保持學習,謹記謙虛。
如果你覺得這件事兒沒意義翻譯的又差,盡情的踩吧。如果你覺得值得鼓勵,感謝留下你的贊,祝各位愛技術的園友在今后每一次應該猛烈突破的時候,不選擇知難而退。在每一次應該獨立思考的時候,不選擇隨波逐流,應該全力以赴的時候,不選擇盡力而為,不辜負每一秒存在的意義。
本文版權歸博客園和作者吳雙本人共同所有。 轉載和爬蟲請注明原文鏈接http://www.cnblogs.com/tdws/p/5617242.html,博客園 蝸牛 2016年6月26日。
如果我們在一個耗時的操作中使用異步的代碼,在其執行過程中,我們不需要無謂的等待。這種方式和在耗時操作的整個執行過程中的阻塞型代碼是相對的。
我們所說的耗時操作包括:
·網絡請求
·硬盤數據訪問
·延遲一段時間的操作
全部的區別在於正在運行代碼的線程。在廣泛應用的編程語言當中,你的代碼運行在操作系統的線程中。如果在遇到耗時操作時,你的線程可以繼續去做其他事情,這就是異步編程。如果你的線程除了等待什么也不做,那就是同步的或者說是阻塞型代碼。
當然我們還有第三種方式去處理耗時操作—輪詢。這是一種不斷重復“詢問”耗時操作是否完成的操作。盡管它在處理段時間操作上有自己的地位,但這通常不是一個好的解決方案。
你也許在過去的工作中使用過異步編程。你可能開啟一個新的線程或者使用線程池,這也是異步編程,因為你所工作的線程可以不被阻塞地去繼續做其他事情。而你的console app,像Console.ReadLine(),這就是阻塞型,在web app中,如果也是這樣的設計等待用戶輸入,將會是多么糟糕呀。
異步編程中很常見的難點在於這個操作在什么時候結束,以便於執行下一步的某些操作。但是這在阻塞型代碼中,很容易做到:你只需要在將接下來的代碼寫在耗時操作的下一行就好。如果不加以處理,這種方式在異步的世界中是怎樣也行不通的。因為幾乎可以確定的是,你下一行代碼是在耗時操作完成之前就已經執行了。
為了解決這個問題,我們發明了一些方式,為了在后台操作完成后去執行下一步操作:
·將下一步操作所需的代碼插在耗時操作代碼主體的后面
·注冊一個當耗時操作結束時會觸發的方法
·在完成后傳遞委托或者Lambad(回調)
如果你下一個操作需要在特定的線程上執行(例如Winform和WPF的UI線程),你還需要在這個線程上安排隊列排序,這是很復雜的。
異步編程釋放了它開始的線程,這有許多原因確實不錯。首先,線程占用並且占用很少的資源,通常只用一個線程就可以完成主要的工作,就像UI線程,但是如果你不盡快釋放它,你的app就會出現未響應狀態。我們將會討論更多的原因在下一章。
最重要的,也是最令我激動的一點是:異步編程讓我們得以有機會去盡情享受計算機並行計算帶來的好處。異步編程讓我們以新的並且合理的方式構建應用程序,用更細粒度的並行和無需編寫難以維護的復雜代碼。第十章將會詳細探討此可能性。
在C#5.0當中,微軟編譯器團隊為我們增加了一個強大的新功能。
它以兩個新型關鍵字出現:。
·async
·await
它當然依賴於一些環境,要求你使用.NET FrameWork4.5,才能你的async代碼有用。
Async是屬於C#編譯器的一個功能,不能被封裝到一個類庫,它對你的源代碼進行改造,就像在早期C#版本對Lambda和迭代器所做的事情一樣。
通過免去早期C#版本異步編程所需的復雜模式和代碼,這個新功能使異步變得非常簡單。有了這個功能,我們可以合理地用異步編程的風格編寫整個項目。
異步編程一直在C#中是可行的的,它以前涉及編程者大量的手工工作,現在C#的async關鍵出現后,異步編程的使用變得非常容易。
Async功能提供了一種讓你表達在耗時操作后需要做什么事情(執行什么代碼)的方式,並且它易讀易懂,表現為異步編程。
Async方法被編譯器轉化的像你平時所寫的阻塞代碼,這里有一段簡單的下載網頁的阻塞型代碼:
private void DumpWebPage(string uri) { WebClient webClient = new WebClient(); string page = webClient.DownloadString(uri); Console.WriteLine(page); }
這里還有一段使用Async實現相同功能的代碼:
private asyncvoid DumpWebPageAsync(string uri) { WebClient webClient = new WebClient(); string page = awaitwebClient.DownloadStringTaskAsync(uri); Console.WriteLine(page); }
兩段代碼在表面上看起來是非常相似的,但是在其外表下,有很大的不同。
被標記為Async的方法,要求方法使用await關鍵字,為了遵循慣例,我們也再方法的后綴名加上了Async。
有趣的地方是await關鍵字,當編譯器遇到它時,他將方法分開(chop the method up),事實上它是很復雜的,所以現在我介紹一個我覺得更易於理解的簡單情況的假結構。
1.await后所有的代碼被分離到另一個方法。
2.我們使用一個新版本叫做DownloadStringTaskAsync的DownloadString方法,它做和原版相同的事情,但它是異步的。
3.這意味着我們可以給它新的第二種方法,即在它完成時它會調用。我們使用一些“魔法”來做這件事,稍后我會告訴你。
4.當下載結束,它將會把我們調用回來帶着已經下載好的可以使用的string字符串,在這種情況下,寫到控制台。
//這就是await分解的方法,上文所說的假的結構(譯者博客園蝸牛注解) private void DumpWebPageAsync(string uri) { WebClient webClient = new WebClient(); webClient.DownloadStringTaskAsync(uri)<- magic(SecondHalf); //魔力的方法調回來 } private void SecondHalf(string awaitedResult) { string page = awaitedResult; Console.WriteLine(page); }
當它運行此代碼時,調用線程會發生什么?當線程抵達DownloadStringTaskAsync方法,下載工作開始,但並不在此線程中執行,在這個線程上,我們抵達了方法的結尾或者說是return,這個線程下一步做什么由我們調用者來決定。如果是UI線程,它將會返回執行用戶操作,除此外,它的資源將會被釋放,這意味着我們在做異步編程!
異步代碼被微軟編譯器開發團隊盡可能設計的像你常寫的阻塞(同步)代碼,我們可以把耗時操作或者遠程操作處理地像本地操作和快速。但是保持和異步調用一樣的性能和優勢。
然而,這樣的設計不是讓你忘記Async是后台操作還有發生回調。你需要小心很多事情,包括:
·異常和try-catch-finally模塊
·方法的返回值
·線程和上下文
·性能
如果不了解它真正發生了什么,你的應用程序可能會意想不到的掛掉,並且你將無法理解異常信息和沒有能力去解決問題。
終於翻譯好了第一章,四頁,花了幾個小時。昨天讀了一遍,沒讀懂,今天翻譯一遍果然收獲頗多。如果您表示支持,給個贊吧!繼續閱讀下一章 我們有什么理由使用Async異步編程
如果我的點滴分享對您有點低幫助,歡迎點擊下方紅色關注,我將持續分享,共同進步