使用 Python 進行并發編程:我為什么不喜歡 Gevent

yangdebin84 8年前發布 | 21K 次閱讀 gevent 并發 Python開發

在Python的發展歷史中,有過一些失敗的修復CPython的缺陷和提高性能的嘗試,比如消除GIL、Stackless(一個微線程擴展,避免傳統線程所帶來的性能與復雜度問題)、psyco (被PyPy代替)、 Unladen Swallow 。當然也有少數成功的,比如PyPy。

協程

在開始表達我的觀點之前,我們先來了解下Coroutine。

Coroutine 也就是 corporate routine,直譯為「協同的例程」,中文一般叫做「協程」, 實際上這個概念和進程與線程有相似之處, 因為linux線程就是所謂的「輕量級進程」。

我在 gevent源碼分析 中找到一段表述的比較好的描述進程和協程異同的內容:

相同點:

二者都是可以看做是一種執行流, 該執行流可以掛起,并且在將來又可以在 你掛起的地方恢復執行, 這實際上都可以看做是continuation, 我們來看看當我們掛 起一個執行流時我們要保存的東西:

  1. 棧, 因為如果你不保存棧,那么局部變量你就無法恢復,同時函數的調用鏈你也無 法恢復,

  2. 寄存器的狀態: 這好理解, 比如說EIP,如果你不保存,那么你恢復執行流就不知道 到底執行哪一條指令, 在比如說ESP,EBP, 如果你不保存,那么你即便有完整的棧 你也不知道怎么用. 這二者實際就是所謂的上下文,也可以說是continuation. 在執行流切換時必須保存 這兩個東西, 內核調度進程時也是一回事.

不同點:

  1. 執行流的調度者不同, 進程是內核調度, 而協程是在用戶態調度, 也就是說進程 的上下文是在內核態保存恢復的,而協程是在用戶態保存恢復的. 很顯然用戶態的 代價更低

  2. 進程會被搶占,而協程不會,也就是說協程如果不主動讓出CPU,那么其他的協程是不 可能得到執行機會,這實際和早期的操作系統類似,比如DOS, 它有一個yield原語, 一個進程調用yield,那么它就會讓出CPU, 其他的進程也就有機會執行了, 如果一 個進程進入了死循環,那么整個系統也就掛起了,永遠無法運行其他的進程了, 但 對協程而言,這不是問題

  3. 對內存的占用不同,實際上協程可以只需要4K的棧就夠了, 而進程占用的內存要大 的多.

  4. 從操作系統的角度講, 多協程的程序是單線程,單進程的

那用一句話描述協程的優勢就是 由開發者決定協程的切換,操作系統無法干預切換,且占用內存少的多 。

Gevent是一種基于協程的Python網絡庫,它用到Greenlet提供的,封裝了libevent事件循環的高層同步API。它讓開發者在不改變編程習慣的同時,用同步的方式寫異步I/O的代碼。

在12-13年的時候,我也用過gevent做過一些爬蟲、網絡編程的工作。在我使用場景中,使用Gevent的性能確實要比用傳統的線程高,甚至高很多。

但是發現Gevent直到現在也仍然受到國人的喜歡( 給Gevent點贊的程序員大概一半是國人 )。雖然我在《Python Web開發實戰》一書中也有專門的一節介紹Gevent,但是我卻不建議大家在生產環境中用它(但是我不反對協程)。

現在每天在Github上都會出現很多有意思的新的項目,每個項目都號稱解決了XXX問題,帶來了YY的思路,令人激動和振奮... 但一般人都能預測它最后會不會大火,也不知道它最后會不會無疾而終。我也曾經在這些不斷新的知識和項目的沖擊下迷失,漸漸的總結了一些有用的判斷標準:

  1. 看項目社區的繁盛情況。

  2. 看Python社區和核心開發者對它的態度,比如在公開場合贊同/反對,給項目貢獻代碼等。

  3. 看業界有沒有真的「大牛」站隊,義務宣傳它

  4. 有沒有在復雜和高負荷的生產環境使用的案例。

如果你愿意花時間,你會知道Gvanrossum從來不喜歡Gevent,而是更愿意另辟蹊徑的實現asyncio(基于生成器的協程)。我找幾個鏈接:

  1. Why not coroutines?

  2. The definitive answer (according to @glyph) on why not gevent.

  3. Async I/O for Python 3

Gvanrossum提到了我大部分贊同,剩下的無感也是由于理解不夠深入或者還「沒踩過坑」。列舉下我的觀點:

  1. Monkey-patching。中文「猴子補丁」,常用于對測試環境做一些hack。我個人不太喜歡這種「黑魔法」,因為如果其他人不了解細節,極為容易產生困惑。Gvanrossum說用它就是"patch-and-pray",太形象了。由于Gevent直接修改標準庫里面大部分的阻塞式系統調用,包括socket、ssl、threading和 select等模塊,而變為協作式運行。但是我們無法保證你在復雜的生產環境中有哪些地方使用這些標準庫會由于打了補丁而出現奇怪的問題,那么你只能祈禱(pray)了。其次,在Python之禪中明確說過:「Explicit is better than implicit.」,猴子補丁明顯的背離了這個原則。最后,Gvanrossum說Stackless之父Christian Tismer也贊同他。 我喜歡顯式的「yield from」

  2. 第三方庫支持。得確保項目中用到其他用到的網絡庫也必須使用純Python或者明確說明支持Gevent,而且就算有這樣的第三方庫,我還會擔心這個第三方庫的代碼質量和功能性。

  3. Greenlet不支持Jython和IronPython,這樣就無法把gevent設計成一個標準庫了。

之前是沒有選擇,很多人選擇了Gevent,而現在明確的有了更正統的、正確的選擇:asyncio(下一篇會詳細介紹)。所以建議大家放棄Gevent,擁抱asyncio。

BTW,《Python之禪》還有一句「practicality beats purity」,如果我上面說的這些問題你都有能力解決,或者知道現在以及未來不會給你造成困擾,那么用Gevent也是可以的。

 

 

來自:http://mp.weixin.qq.com/s/PbvrRZAyiLB0ZUk0VzH2IQ

 

 本文由用戶 yangdebin84 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!