Python的asyncio是使用 async/await 語法編寫併發代碼的標準庫。經過上一節的講解,咱們瞭解了它不斷變化的發展歷史。到了Python最新穩定版 3.7 這個版本,asyncio又作了比較大的調整,把這個庫的API分爲了 高層級API和低層級API,並引入asyncio.run()這樣的高級方法,讓編寫異步程序更加簡潔。shell
本節但願提綱挈領地介紹最新 3.7 版的asnycio,先從全局認識Python這個異步IO庫。編程
asyncio的高層級API主要提升以下幾個方面:安全
asyncio的低層級API用以支持開發異步庫和框架:網絡
高層級API讓咱們更方便的編寫基於asyncio的應用程序。這些API包括:架構
協程經過 async/await 語法進行聲明,是編寫異步應用的推薦方式。歷史的 @asyncio.coroutine
和 yield from
已經被棄用,並計劃在Python 3.10中移除。協程能夠經過 asyncio.run(coro, *, debug=False)
函數運行,該函數負責管理事件循環並完結異步生成器。它應該被用做asyncio程序的主入口點,至關於main函數,應該只被調用一次。併發
任務被用於併發調度協程,可用於網絡爬蟲的併發。使用 asyncio.create_task()
就能夠把一個協程打包爲一個任務,該協程會自動安排爲很快運行。框架
協程,任務和Future都是可等待對象。其中,Future是低層級的可等待對象,表示一個異步操做的最終結果。異步
流是用於網絡鏈接的高層級的使用 async/await的原語。流容許在不使用回調或低層級協議和傳輸的狀況下發送和接收數據。異步讀寫TCP有客戶端函數 asyncio.open_connection()
和 服務端函數 asyncio.start_server()
。它還支持 Unix Sockets: asyncio.open_unix_connection()
和 asyncio.start_unix_server()
。async
asyncio同步原語的設計相似於threading模塊的原語,有兩個重要的注意事項:
asyncio原語不是線程安全的,所以它們不該該用於OS線程同步(而是用threading)
這些同步原語的方法不接受超時參數; 使用asyncio.wait_for()
函數執行超時操做。
asyncio具備如下基本同步原語:分佈式
asyncio提供了經過 async/await 建立和管理子進程的API。不一樣於Python標準庫的subprocess,asyncio的子進程函數都是異步的,而且提供了多種工具來處理這些函數,這就很容易並行執行和監視多個子進程。建立子進程的方法主要有兩個:
coroutine asyncio.create_subprocess_exec()
coroutine asyncio.create_subprocess_shell()
asyncio 隊列的設計相似於標準模塊queue的類。雖然asyncio隊列不是線程安全的,但它們被設計爲專門用於 async/await 代碼。須要注意的是,asyncio隊列的方法沒有超時參數,使用 asyncio.wait_for()
函數進行超時的隊列操做。
由於和標註模塊queue的類設計類似,使用起來跟queue無太多差別,只須要在對應的函數前面加 await 便可。asyncio 隊列提供了三種不一樣的隊列:
asyncio提供了幾種異常,它們是:
低層級API爲編寫基於asyncio的庫和框架提供支持,有意編寫異步庫和框架的大牛們須要熟悉這些低層級API。主要包括:
事件循環是每一個asyncio應用程序的核心。 事件循環運行異步任務和回調,執行網絡IO操做以及運行子進程。
應用程序開發人員一般應該使用高級asyncio函數,例如asyncio.run()
,而且不多須要引用循環對象或調用其方法。
Python 3.7 新增了 asyncio.get_running_loop()
函數。
Future對象用於將基於低層級回調的代碼與高層級的 async/await 代碼進行橋接。
Future表示異步操做的最終結果。 不是線程安全的。
Future是一個可等待對象。 協程能夠等待Future對象,直到它們有結果或異常集,或者直到它們被取消。
一般,Futures用於啓用基於低層級回調的代碼(例如,在使用asyncio傳輸實現的協議中)以與高層級 async/await 代碼進行互操做。
Transport 和 Protocol由低層級事件循環使用,好比函數loop.create_connection()
。它們使用基於回調的編程風格,並支持網絡或IPC協議(如HTTP)的高性能實現。
在最高級別,傳輸涉及字節的傳輸方式,而協議肯定要傳輸哪些字節(在某種程度上什麼時候傳輸)。
換種方式說就是:傳輸是套接字(或相似的I/O端點)的抽象,而協議是從傳輸的角度來看的應用程序的抽象。
另外一種觀點是傳輸和協議接口共同定義了一個使用網絡I/O和進程間I/O的抽象接口。
傳輸和協議對象之間始終存在1:1的關係:協議調用傳輸方法來發送數據,而傳輸調用協議方法來傳遞已接收的數據。
大多數面向鏈接的事件循環方法(例如loop.create_connection()
)一般接受protocol_factory參數,該參數用於爲接受的鏈接建立Protocol對象,由Transport對象表示。 這些方法一般返回(傳輸,協議)元組。
事件循環策略是一個全局的按進程劃分的對象,用於控制事件循環的管理。 每一個事件循環都有一個默認策略,可使用策略API對其進行更改和自定義。
策略定義了上下文的概念,並根據上下文管理單獨的事件循環。 默認策略將上下文定義爲當前線程。
經過使用自定義事件循環策略,能夠自定義get_event_loop()
,set_event_loop()
和new_event_loop()
函數的行爲。
asyncio模塊設計爲可移植的,但因爲平臺的底層架構和功能,某些平臺存在細微的差別和限制。在Windows平臺,有些是不支持的,好比 loop.create_unix_connection()
and loop.create_unix_server()
。而Linux和比較新的macOS所有支持。
Python 3.7 經過對asyncio分組使得它的架構更加清晰,普通寫異步IO的應用程序只需熟悉高層級API,須要寫異步IO的庫和框架時才須要理解低層級的API。