現在的位置: 首頁 > 編程語言 > 正文

如何使用Python中的asyncio

2020年02月21日 編程語言 ⁄ 共 2480字 ⁄ 字型大小 評論關閉

  Python的非同步編程功能(簡稱async)讓你可以編寫不必等待獨立任務完成就可以完成更多工作的程序。Python附帶的asyncio庫為你提供了使用async處理磁碟或網路I/O、無需其他方面等待的工具。

  asyncio提供了兩種處理非同步操作的API:高級和低級。高級API用途廣泛,適用於各種應用程序。低級API功能強大,但也很複雜,使用頻率較低。

  本文重點介紹高級API。我們會逐步介紹asyncio中常用的高級API,說明它們如何可用於涉及非同步任務的常見操作。

  在Python中運行協程和任務

  很自然,asyncio最常見的用途是運行Python腳本的非同步部分。這意味著學會使用協程和任務。

  Python的非同步組件(包括協程和任務)只能與其他非同步組件一起使用,不能與常規的同步Python一起使用,因此需要asyncio來填補缺口。為此,你要使用asyncio.run函數:

  import asyncio

  async def main():

  print ("Waiting 5 seconds. ")

  for _ in range(5):

  await asyncio.sleep(1)

  print (".")

  print ("Finished waiting.")

  asyncio.run(main())

  這運行main(),連同main()觸發的任何常式,等待結果返回。

  通常而言,Python程序應只有一個.run()語句,就像Python程序應只有一個main()函數一樣。 如果不小心使用,async可能會使程序的控制流難以閱讀。程序的非同步代碼只有一個入口點可以避免情況變得繁複。

  非同步函數還可以調度安排成tasks,即包裝協程並幫助運行協和的對象。

  async def my_task():

  do_something()

  task = asyncio.create_task(my_task())

  my_task()隨後在事件循環中運行,結果存儲在task中。

  如果你只有一個任務想要獲取結果,可以使用asyncio.wait_for(task)來等待任務完成,然後使用task.result()檢索結果。但如果你安排了許多任務要執行,並想要等待所有任務完成,不妨使用asyncio.wait([task1, task2])收集結果。(注意,如果你不希望超過一定長度的時間後運行,可以設置操作的超時時間。)

  在Python中管理非同步事件循環

  asyncio的另一個常見用途是管理非同步事件循環。事件循環是運行非同步函數和回調的對象。使用asyncio.run()時,它自動創建。你通常希望每個程序僅使用一個非同步事件循環,同樣以便管理。

  如果你在編寫伺服器等更高級的軟體,需要對事件循環擁有較低級別的訪問權。為此,你可以「揭開面紗」,直接接觸事件循環的內部機制。不過如果是簡單的工作,不需要這麼做。

  在Python中使用streams讀寫數據

  async的最佳使用場景是長時間運行的網路操作,其中應用程序可能阻止等待其他某個資源返回結果。為此,asyncio提供了streams,這是用於執行網路I/O的高級機制。這包括充當網路請求的伺服器。

  asyncio使用兩個類StreamReader和StreamWriter,在高級層面進行網路讀寫。如果你要從網路讀取,可以使用asyncio.open_connection()打開連接。該函數返回StreamReader對象和StreamWriter對象的元組,你要在每個對象上使用.read() 和.write()方法以便通信。

  想接收來自遠程主機的連接,使用asyncio.start_server()。asyncio.start_server()函數將回調函數client_connected_cb作為參數來接受,只要收到請求就調用該函數。該回調函數將StreamReader和StreamWriter的實例作為參數,那樣你就能處理伺服器的讀/寫邏輯。這個例子( https://gist.github.com/ethanfrey/75e58db27095936b9e5e )介紹了一個簡單的HTTP伺服器使用asyncio驅動的aiohttp庫。

  在Python中同步任務

  非同步任務往往獨立運行,但有時你希望它們彼此通信。asyncio提供了隊列和另外幾種在任務之間進行同步的機制:

  隊列:asyncio隊列允許非同步函數排列Python對象,以便供其他非同步函數使用——比如說,基於行為在不同類型的函數之間分配工作負載。

  同步原語:asyncio中的鎖、事件、條件和信號其工作方式類似常規的Python鎖、事件、條件和信號。

  關於所有這些方法要記住的一點是它們不是線程安全的。對於在同一事件循環中運行的非同步任務來說這不是問題。但如果你試圖與不同事件循環、操作系統線程或進程中的任務共享信息,就需要使用threading模塊及其對象來執行此操作。

  此外,如果你想跨線程邊界啟動協程,請使用asyncio.run_coroutine_threadsafe()函數,然後將與它結合使用的事件循環作為參數傳遞。

  在Python中暫停協程

  asyncio的另一個常見、但很少討論的用途是在協程內部等待任意時長。為此你不能使用time.sleep(),否則會阻塞整個程序。而是應使用asyncio.sleep(),它允許其他協程繼續運行。

  在Python中使用較低級別的async

  最後,如果你認為構建的應用程序可能需要asyncio的較低級組件,在開始編程之前先考慮一番:很可能有人已經構建了可以滿足你需求的基於async的Python庫。

  比如說,如果你需要非同步DNS查詢,不妨查看aiodns庫;若是非同步SSH會話,則有asyncSSH。通過關鍵字「async」(以及其他與任務相關的關鍵字)搜索PyPI,或查看人工篩選的Awesome Asyncio列表以獲取靈感。

抱歉!評論已關閉.