摘要:創建第一個協程推薦使用語法來聲明協程,來編寫異步應用程序。協程兩個緊密相關的概念是協程函數通過定義的函數協程對象調用協程函數返回的對象。它是一個低層級的可等待對象,表示一個異步操作的最終結果。
我們講以Python 3.7 上的asyncio為例講解如何使用Python的異步IO。
創建第一個協程Python 3.7 推薦使用 async/await 語法來聲明協程,來編寫異步應用程序。我們來創建第一個協程函數:首先打印一行“你好”,等待1秒鐘后再打印“猿人學”。
sayhi()函數通過 async 聲明為協程函數,較之前的修飾器聲明更簡潔明了。
在實踐過程中,什么功能的函數要用async聲明為協程函數呢?就是那些能發揮異步IO性能的函數,比如讀寫文件、讀寫網絡、讀寫數據庫,這些都是浪費時間的IO操作,把它們協程化、異步化從而提高程序的整體效率(速度)。
sayhi()函數是通過?asyncio.run()來運行的,而不是直接調用這個函數(協程)。因為,直接調用并不會把它加入調度日程,而只是簡單的返回一個協程對象:
那么,如何真正運行一個協程呢?asyncio 提供了三種機制:
(1)asyncio.run() 函數,這是異步程序的主入口,相當于C語言中的main函數。
(2)用await等待協程,比如上例中的?await asyncio.sleep(1)?。再看下面的例子,我們定義了協程?say_delay()?,在main()協程中調用兩次,第一次延遲1秒后打印“你好”,第二次延遲2秒后打印“猿人學”。這樣我們通過 await 運行了兩個協程。
從起止時間可以看出,兩個協程是順序執行的,總共耗時1+2=3秒。
(3)通過?asyncio.create_task()?函數并發運行作為 asyncio 任務(Task) 的多個協程。下面,我們用create_task()來修改上面的main()協程,從而讓兩個say_delay()協程并發運行:
從運行結果的起止時間可以看出,兩個協程是并發執行的了,總耗時等于最大耗時2秒。
asyncio.create_task()?是一個很有用的函數,在爬蟲中它可以幫助我們實現大量并發去下載網頁。在Python 3.6中與它對應的是?ensure_future()。
可等待對象(awaitables)可等待對象,就是可以在 await 表達式中使用的對象,前面我們已經接觸了兩種可等待對象的類型:協程和任務,還有一個是低層級的Future。
asyncio模塊的許多API都需要傳入可等待對象,比如 run(), create_task() 等等。
(1)協程協程是可等待對象,可以在其它協程中被等待。協程兩個緊密相關的概念是:
協程函數:通過 async def 定義的函數;
協程對象:調用協程函數返回的對象。
運行上面這段程序,結果為:
co is now is 1548512708.2026224 now is 1548512708.202648
可以看到,直接運行協程函數 whattime()得到的co是一個協程對象,因為協程對象是可等待的,所以通過 await 得到真正的當前時間。now2是直接await 協程函數,也得到了當前時間的返回值。
(2)任務前面我們講到,任務是用來調度協程的,以便并發執行協程。當一個協程通過?asyncio.create_task()?被打包為一個 任務,該協程將自動加入程序調度日程準備立即運行。
create_task()的基本使用前面例子已經講過。它返回的task通過await來等待其運行完。如果,我們不等待,會發生什么?“準備立即運行”又該如何理解呢?先看看下面這個例子:
運行這段代碼的情況是這樣的:
首先,1秒鐘后打印一行,這是第13,14行代碼運行的結果:
calling:0, now is 09:15:15
接著,停頓1秒后,連續打印4行:
calling:1, now is 09:15:16 calling:2, now is 09:15:16 calling:3, now is 09:15:16 calling:4, now is 09:15:16
從這個結果看,asyncio.create_task()產生的4個任務,我們并沒有await,它們也執行了。關鍵在于第18行的?await,如果把這一行去掉或是sleep的時間小于1秒(比whattime()里面的sleep時間少即可),就會只看到第一行的輸出結果而看不到后面四行的輸出。這是因為,main()不sleep或sleep少于1秒鐘,main()就在whattime()還未來得及打印結果(因為,它要sleep 1秒)就退出了,從而整個程序也退出了,就沒有whattime()的輸出結果。
再來理解一下“準備立即執行”這個說法。它的意思就是,create_task()只是打包了協程并加入調度隊列還未執行,并準備立即執行,什么時候執行呢?在“主協程”(調用create_task()的協程)掛起的時候,這里的“掛起”有兩個方式:
一是,通過 await task 來執行這個任務;
另一個是,主協程通過 await sleep 掛起,事件循環就去執行task了。
我們知道,asyncio是通過事件循環實現異步的。在主協程 main()里面,沒有遇到 await 時,事件就是執行main()函數,遇到 await 時,事件循環就去執行別的協程,即create_task()生成的whattime()的4個任務,這些任務一開始就是 await sleep 1秒。這時候,主協程和4個任務協程都掛起了,CPU空閑,事件循環等待協程的消息。
如果main()協程只sleep了0.1秒,它就先醒了,給事件循環發消息,事件循環就來繼續執行main()協程,而main()后面已經沒有代碼,就退出該協程,退出它也就意味著整個程序退出,4個任務就沒機會打印結果;
如果main()協程sleep時間多余1秒,那么4個任務先喚醒,就會得到全部的打印結果;
如果main()的18行sleep等于1秒時,和4個任務的sleep時間相同,也會得到全部打印結果。這是為什么呢?
我猜想是這樣的:4個任務生成在前,第18行的sleep在后,事件循環的消息響應可能有個先進先出的順序。后面深入asyncio的代碼專門研究一下這個猜想正確與否。
(3)Future它是一個低層級的可等待對象,表示一個異步操作的最終結果。目前,我們寫應用程序還用不到它,暫不學習。
asyncio異步IO協程總結協程就是我們異步操作的片段。通常,寫程序都會把全部功能分成很多不同功能的函數,目的是為了結構清晰;進一步,把那些涉及耗費時間的IO操作(讀寫文件、數據庫、網絡)的函數通過 async def 異步化,就是異步編程。
那些異步函數(協程函數)都是通過消息機制被事件循環管理調度著,整個程序的執行是單線程的,但是某個協程A進行IO時,事件循環就去執行其它協程非IO的代碼。當事件循環收到協程A結束IO的消息時,就又回來執行協程A,這樣事件循環不斷在協程之間轉換,充分利用了IO的閑置時間,從而并發的進行多個IO操作,這就是異步IO。
寫異步IO程序時記住一個準則:需要IO的地方異步。其它地方即使用了協程函數也是沒用的。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/43851.html
摘要:并發的方式有多種,多線程,多進程,異步等。多線程和多進程之間的場景切換和通訊代價很高,不適合密集型的場景關于多線程和多進程的特點已經超出本文討論的范疇,有興趣的同學可以自行搜索深入理解。 編程中,我們經常會遇到并發這個概念,目的是讓軟件能充分利用硬件資源,提高性能。并發的方式有多種,多線程,多進程,異步IO等。多線程和多進程更多應用于CPU密集型的場景,比如科學計算的時間都耗費在CPU...
摘要:具有以下基本同步原語子進程提供了通過創建和管理子進程的。雖然隊列不是線程安全的,但它們被設計為專門用于代碼。表示異步操作的最終結果。 Python的asyncio是使用 async/await 語法編寫并發代碼的標準庫。通過上一節的講解,我們了解了它不斷變化的發展歷史。到了Python最新穩定版 3.7 這個版本,asyncio又做了比較大的調整,把這個庫的API分為了 高層級API和...
摘要:理解迭代對象迭代器生成器后端掘金本文源自作者的一篇博文,原文是,俺寫的這篇文章是按照自己的理解做的參考翻譯。比較的是兩個對象的內容是后端掘金黑魔法之協程異步后端掘金本文為作者原創,轉載請先與作者聯系。 完全理解關鍵字with與上下文管理器 - 掘金如果你有閱讀源碼的習慣,可能會看到一些優秀的代碼經常出現帶有 with 關鍵字的語句,它通常用在什么場景呢?今天就來說說 with 和 上下...
摘要:以下這些項目,你拿來學習學習練練手。當你每個步驟都能做到很優秀的時候,你應該考慮如何組合這四個步驟,使你的爬蟲達到效率最高,也就是所謂的爬蟲策略問題,爬蟲策略學習不是一朝一夕的事情,建議多看看一些比較優秀的爬蟲的設計方案,比如說。 (一)如何學習Python 學習Python大致可以分為以下幾個階段: 1.剛上手的時候肯定是先過一遍Python最基本的知識,比如說:變量、數據結構、語法...
閱讀 1391·2023-04-26 03:04
閱讀 2325·2019-08-30 15:44
閱讀 3727·2019-08-30 14:15
閱讀 3507·2019-08-27 10:56
閱讀 2702·2019-08-26 13:53
閱讀 2616·2019-08-26 13:26
閱讀 3075·2019-08-26 12:11
閱讀 3609·2019-08-23 18:21