[python異步 第三篇 ] python事件循環庫的發展歷史

Python的異步IO

異步IO的優勢顯而易見,各種語言都通過實現這個機制來提高自身的效率,Python也不例外。

一、Python 2的異步IO庫

Python 2 時代官方並沒有異步IO的支持,但是有幾個第三方庫通過事件或事件循環(Event Loop)實現了異步IO,它們是:

twisted: 是事件驅動的網絡庫
gevent: greenlet + libevent(後來是libev或libuv)。通過協程(greenlet)和事件循環庫(libev,libuv)實現的gevent使用很廣泛。
tornado: 支持異步IO的web框架。自己實現了IOLOOP。

二、Python 3 官方的異步IO

Python 3.4 加入了asyncio 庫,使得Python有了支持異步IO的官方庫。這個庫,底層是事件循環(EventLoop),上層是協程和任務。asyncio自從3.4 版本加入到最新的 3.7版一直在改進中。

Python 3.4 剛開始的asyncio的協程還是基於生成器的,通過 yield from 語法實現,可以通過裝飾器 @asyncio.coroutine (已過時)裝飾一個函數來定義一個協程。比如:
asyncio

Python 3.5 引入了兩個新的關鍵字 await 和 async 用來替換 @asyncio.coroutine 和 yield from ,從語言本身來支持異步IO。從而使得異步編程更加簡潔,並和普通的生成器區別開來。

注意: 對基於生成器的協程的支持已棄用,並計劃在 Python 3.10 中移除。所以,寫異步IO程序時只需使用 async 和 await 即可。

Python 3.7 又進行了優化,把API分組爲高層級API和低層級API。 我們先看看下面的代碼,發現與上面的有什麼不同?
asyncio-python
除了用 async 替換 @asyncio.coroutine 和用 await 替換 yield from 外,最大的變化就是關於eventloop的代碼不見了,只有一個 async.run()。這就是 3.7 的改進,把eventloop相關的API歸入到低層級API,新引進run()作爲高層級API讓寫應用程序的開發者調用,而不用再關心eventloop。除非你要寫異步庫(比如MySQL異步庫)纔會和eventloop打交道。

需要注意的是, async.run() 是3.7版新增加的,處於暫定API狀態。 暫定API,是指被有意排除在標準庫的向後兼容性保證之外的應用編程接口。雖然此類接口通常不會再有重大改變,但只要其被標記爲暫定,就可能在覈心開發者確定有必要的情況下進行向後不兼容的更改(甚至包括移除該接口)。此種更改並不會隨意進行 — 僅在 API 被加入之前未考慮到的嚴重基礎性缺陷被發現時纔可能會這樣做。即便是對暫定 API 來說,向後不兼容的更改也會被視爲“最後的解決方案” —— 任何問題被確認時都會盡可能先嚐試找到一種向後兼容的解決方案。這種處理過程允許標準庫持續不斷地演進,不至於被有問題的長期性設計缺陷所困。

從上面關於 asyncio 的發展來看它一直在變化,3.4,3.5,3.6, 3.7 都有很多細節上的變化。當我看到3.7的run()函數時,也發現一年前基於3.6的asnycio寫的爬蟲不那麼優雅了。

這種變化,一方面改善了asyncio本身的性能和使用方便程度,但另一方面也增加了我們使用者的學習成本、Python升級帶來的改造的成本。如果你以消極的態度抵制這種變化,可以去學習golang,C++來實現你的程序;如果你以積極的態度迎接這種變化,可以更快的掌握這種變化,並優雅 高效的實現你的程序。

只要你喜歡用Python寫程序解決問題,那麼就接受並掌握這種變化吧。其實,那種語言不在變,那種技術不在前進。作爲程序員,你只有不斷地學習和前進。

三、uvloop

uvloop是用Cython寫的,基於libuv這個C語言實現的高性能異步I/O庫。asyncio自己的事件循環是用Python寫的,用uvloop替換asyncio自己的事件循環可以使asyncio的速度更快。並且使用相當簡潔:
uvloop

四、總結

  1. 異步IO用在費時的IO操作上以提高程序整體效率。

  2. 同步和異步,阻塞和非阻塞就是方法和現象。

  3. Python的異步歷史很複雜,然而目前給我們用的已經很優雅,記住以下三點:

    • Python 3.7

    • await,async

    • IO的時候用

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章