python協程及應用(一):簡介

前言:我一直在關注如何讓開發變得更容易,效率更高,更不易出錯。之前做過類似的努力,結合SOA和工作流引擎做的一個流程編輯:http://blog.csdn.net/chgaowei/article/category/597366。它有它自身的缺陷:腳本和程序之間的耦合太大,腳本限制太多。
後來接觸並且學習Python,它簡潔而強大,應該是解決這類問題的一個思路。那天看到Python自帶的生成器yield關鍵字,感覺着應該是解決邏輯問題的一個突破點(後面會介紹)。再後來參加Python con2011 china,看裏面很多人提到了協程和Greenlet,特別是賴勇浩提出的口號:協程纔是未來。當時很興奮,我一直思考的問題有了解決方案。

協程是什麼?
可以參考一下維基百科:http://zh.wikipedia.org/wiki/%E5%8D%8F%E7%A8%8B
我之前寫過一篇文章:併發編程學習總結也提到過。我認爲這個定義也許更加貼切:“編程語言提供的一種併發機制”。
先讓我們來看一下進程和線程。
未來讓計算機能夠同時處理多個任務,操作系統有了進程的概念,而且在進程內部,基本可以認爲當前系統只有一個進程在運行,操作系統對此作了非常好的封裝。進程間的切換是有操作系統來完成的。
進程有一個問題,就是進程間切換耗費計算機資源非常大,而且申請一個新的進程的成本也非常高。所以後來就有了線程,它生成的成本和切換的消耗都比進程要低很多,而且線程間通信也非常方便。
線程的問題是:1)存在線程安全問題,出了問題非常不易定位。2)進程內部有線程數目的限制。3)隨着併發量的增加,線程生成和切換的成本也變得昂貴。
解決併發還有一個方案是IO多路複用,它的效率確實非常高,但是代碼複雜度也非常高:它把一個流程打散成一個個的節點,散落在多個地方,對開發和維護非常不利(這個是我們經常用的方案)。

好,來看協程是如何解決這些問題的:
1)協程的生成成本更低。其實就是一塊內存,記錄之前的調用的棧信息。你甚至可以通過控制函數調用的層次來進一步降低協程的大小。要生成一個協程,只需要申請一塊內存並賦值。
2)切換更快。基本是就是內存的拷貝的速度。
3)沒有線程安全問題。一個進程內可以同時存在多個協程,但是隻有一個協程是激活的,而且協程的激活和休眠時程序員通過編程來控制,而不是內核來控制的。這樣就沒有了線程安全問題。
4)可讀性更好。相對於IO多路複用來說,你調用的服務接口或者IO接口是異步的,但是你的代碼是流暢(順序)的,並沒有被異步和回調打亂。協程也是異步的,但是它會把異步的事件和回調封裝起來,形成類似遠程調用接口。

一些資料:
賴勇浩在pycon 2011 china上列舉了一個node.js用異步實現的一塊代碼和Python協程風格的代碼很能說明問題:http://www.slideshare.net/laiyonghao/python-webgame-10452102
賴勇浩關於協程的文章非常不錯,可以作爲參考,可惜沒有寫完:協程三篇之一(協程初接觸)
賴勇浩自己也做了一個機遇Google protocol buffer和Greenlet的遠程調用框架abu.rpc,有興趣的可以參考一下(在寫這篇文章之前,還一直沒有抽出時間看一下)。


提供協程機制的編程語言:
現在很多語言都提供協程機制,或者通過第三方模塊來實現協程。目前據我所知,支持協程的語言包括:Python,lua,erlang,go,io,ruby,c#等。我甚至還看到一些文章會在c中使用go和switch或者longjmp/setjmp來實現協程,不過很難再實際項目中使用。

Python的協程實現:
yield可以實現協程。另外,還有很多第三方的版本,比如greenlet。

協程可以用來做什麼?
1)描述邏輯:我主要把協程用來描述邏輯。一個流程可能需要調用多個接口,其中很多接口是異步的。這樣描述起來會困難一點。用線程是可以解決部分問題,但是複雜度提升。
2)提高併發:主要應用在IO密集型應用中。gevent就是在greenlet基礎之上的一個處理併發的框架,和上面的區別是,這裏的事件及接口是IO接口。

缺陷:
無法使用多核。不過可以通過進程+協程來解決。


發佈了291 篇原創文章 · 獲贊 48 · 訪問量 158萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章