Python 實現(xiàn)協(xié)程(四)

我們本節(jié)即將學習的 Python asyncio 包亚铁,使用基于事件循環(huán)驅動的協(xié)程實現(xiàn)并發(fā)座菠。這是 Python 中最大抖韩,也是最具雄心壯志的庫之一漫玄。

既然 asyncio 基于事件驅動劈愚,那么讓我們首先來了解下事件驅動編程瞳遍,再進入正題。

一. 事件驅動

1.1 單線程菌羽、多進程以及事件驅動編程模型的比較

事件驅動編程是一種編程范式掠械,程序的執(zhí)行流程由外部事件來決定。它的特點是包含一個事件循環(huán)注祖,當外部事件發(fā)生時猾蒂,使用一種回調機制來觸發(fā)相應的處理。

此外是晨,單線程同步以及多進程也是常見的編程范式肚菠。下圖對比了單線程、多線程以及事件驅動編程模型罩缴。

上圖中蚊逢,這個程序有 A / B / C 個任務需要完成层扶,每個任務在執(zhí)行過程中都存在 IO 阻塞,阻塞的時間使用黑色塊表示烙荷。

單線程同步模型:多個任務按序執(zhí)行镜会。一旦某個任務因為 I/O 而阻塞,其他所有的任務都必須等待终抽,直到前面的任務完成之后它們才能依次執(zhí)行戳表。即使任務之間并沒有互相依賴,仍然需要等待昼伴,使得程序不必要的降低了運行速度匾旭。

多進程同步模型中:各個任務分別在獨立的進程中執(zhí)行。進程由操作系統(tǒng)來管理圃郊,在多處理器系統(tǒng)上可以并行處理季率,或者在單處理器系統(tǒng)上交錯執(zhí)行。與單線程同步程序相比描沟,多進程的效率更高飒泻,但同時創(chuàng)建進程的資源消耗也比較大。

多線程操作共享資源時吏廉,還需要考慮同步互斥機制泞遗,而且 CPython 解釋器無法利用計算機多核的特性。

事件驅動編程模型中:多個任務在一個單獨的線程中交錯執(zhí)行席覆。當遇到 I/O 操作時史辙,注冊一個回調到事件循環(huán)中,然后當 I/O 操作完成時繼續(xù)執(zhí)行佩伤。

事件循環(huán)輪詢所有的事件聊倔,當事件到來時將它們分配給等待處理事件的回調函數(shù)。因此生巡,一個任務在遇到 IO 阻塞時耙蔑,可以讓步出 CPU 的使用權,讓其它任務繼續(xù)執(zhí)行孤荣,而不是一直等待甸陌。事件驅動編程模型不需要關心線程安全問題。

我們之前介紹的 IO 多路復用盐股,使用的就是事件驅動編程模型钱豁,利用 select / poll / epoll 將 IO 事件交給系統(tǒng)內核監(jiān)控,當某個 IO 描述符結束阻塞準備就緒時疯汁,就將其返回牲尺。

1.2 協(xié)程的引入

事件驅動編程模型有諸多好處,但在嵌套多層回調時幌蚊,可讀性較差谤碳,出現(xiàn)異常排查也很困難凛澎,非常不利于后期的維護。

于是估蹄,我們引入?yún)f(xié)程來解決上面的問題塑煎,允許我們 采用同步的方式去編寫異步的代碼,使代碼的可讀性提升臭蚁,既操作簡單最铁,速度又快

協(xié)程使用單線程去切換任務垮兑,性能遠高于線程切換冷尉,且不需要加鎖,并發(fā)性高系枪。

進程雀哨、線程以及協(xié)程的關系可以使用下圖描述:

進程可以包含多個線程,多個線程共享進程的資源私爷,因此線程比進程更輕量雾棺;而協(xié)程的本質是一個函數(shù),一個線程可以包含多個協(xié)程衬浑,協(xié)程比線程更輕量捌浩。

1.3 相關概念

  • 并發(fā):CPU 在多個任務之間不斷切換,比如在一秒內 CPU 切換了 100 個進程工秩,就可以認為 CPU 的并發(fā)是 100尸饺。
  • 并行:在多核 CPU 中,多個任務在不同的 CPU 上同時運行助币;并行數(shù)量和 CPU 數(shù)量是一致的浪听。
  • 同步:必須等待前一個調用完成后,再開始新的的調用眉菱。
  • 異步:不必等待前一個操作的完成迹栓,就開始新的的調用。
  • 阻塞:調用函數(shù)的時候倍谜,當前線程被掛起迈螟。
  • 非阻塞:調用函數(shù)的時候,當前線程不會被掛起尔崔,而是立即返回結果(不管什么樣的結果)。

二. asyncio 模塊

Python3.4 中引入 asyncio 模塊褥民,創(chuàng)建協(xié)程函數(shù)時使用@asyncio.coroutine 裝飾器裝飾季春。

我們前面介紹的 yield frompython3.4 前的用法,即包含 yield from 語句的函數(shù)即可作為生成器函數(shù)消返,也可以稱作協(xié)程函數(shù)载弄。

Python3.4 之后耘拇,使用 @asyncio.coroutine 裝飾的函數(shù)即可稱作協(xié)程函數(shù)。關于 asyncio 中的基本概念總結如下:

術語 說明
coroutine 協(xié)程對象 使用 @asyncio.coroutine 裝飾器裝飾的函數(shù)被稱作協(xié)程函數(shù)宇攻,它的調用不會立即執(zhí)行惫叛,而是返回一個協(xié)程對象。協(xié)程對象需要包裝成任務注入到事件循環(huán)逞刷,由事件循環(huán)調用嘉涌。
task 任務 使用協(xié)程對象作為參數(shù)創(chuàng)建任務,任務是協(xié)程對象的進一步封裝夸浅,其包含任務的各種狀態(tài)
event_loop 事件循環(huán) 協(xié)程函數(shù)必須添加到事件循環(huán)中仑最,由事件循環(huán)去運行,因為直接調用協(xié)程函數(shù)返回的是協(xié)程對象帆喇,協(xié)程函數(shù)并不會真正開始運行警医。事件循環(huán)控制任務運行流程,是任務的調用方坯钦。

示例 asyncio 實現(xiàn)協(xié)程的簡單示例

import time
import asyncio
 
 
@asyncio.coroutine
def do_some_work():
    print('Coroutine Start.')
    time.sleep(3)  # 模擬IO操作
    print('Print in coroutine.')
 
 
def main():
    start = time.time()
    loop = asyncio.get_event_loop()
    coroutine = do_some_work()
    loop.run_until_complete(coroutine)
    end = time.time()
    print('運行耗時:{:.2f}'.format(end - start))  # 打印程序運行耗時
 
 
if __name__ == '__main__':
    main()

運行結果:首先使用協(xié)程裝飾器 @asyncio.coroutine 創(chuàng)建協(xié)程函數(shù)预皇,協(xié)程函數(shù)中使用 time.sleep(3) 模擬一個耗時的IO操作俊柔。

asyncio.get_event_loop() 用來創(chuàng)建事件循環(huán)此迅;每個線程中只能有一個事件循環(huán)废麻,get_event_loop 獲取當前已經(jīng)存在的事件循環(huán)约巷,如果當前線程中沒有谣膳,則新建一個事件循環(huán)并蝗。

loop.run_until_complete(coroutine) 將協(xié)程對象注入到事件循環(huán)匹中,協(xié)程的運行由事件循環(huán)控制帘瞭。事件循環(huán)的 run_until_complete 方法會阻塞運行洋丐,直到任務全部完成呈昔。

協(xié)程對象作為 run_until_complete 方法的參數(shù),loop 會自動將協(xié)程對象包裝成任務來運行友绝。下節(jié)我們會講到多個任務注入事件循環(huán)的情況堤尾。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市迁客,隨后出現(xiàn)的幾起案子郭宝,更是在濱河造成了極大的恐慌,老刑警劉巖掷漱,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件粘室,死亡現(xiàn)場離奇詭異,居然都是意外死亡卜范,警方通過查閱死者的電腦和手機衔统,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人锦爵,你說我怎么就攤上這事舱殿。” “怎么了险掀?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵沪袭,是天一觀的道長。 經(jīng)常有香客問我樟氢,道長冈绊,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任嗡害,我火速辦了婚禮焚碌,結果婚禮上,老公的妹妹穿的比我還像新娘霸妹。我一直安慰自己十电,他們只是感情好,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布叹螟。 她就那樣靜靜地躺著鹃骂,像睡著了一般。 火紅的嫁衣襯著肌膚如雪罢绽。 梳的紋絲不亂的頭發(fā)上畏线,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天,我揣著相機與錄音良价,去河邊找鬼寝殴。 笑死,一個胖子當著我的面吹牛明垢,可吹牛的內容都是我干的蚣常。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼痊银,長吁一口氣:“原來是場噩夢啊……” “哼抵蚊!你這毒婦竟也來了?” 一聲冷哼從身側響起溯革,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤贞绳,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后致稀,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體冈闭,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年豺裆,在試婚紗的時候發(fā)現(xiàn)自己被綠了拒秘。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片号显。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡臭猜,死狀恐怖躺酒,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情蔑歌,我是刑警寧澤羹应,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站次屠,受9級特大地震影響园匹,放射性物質發(fā)生泄漏。R本人自食惡果不足惜劫灶,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一裸违、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧本昏,春花似錦供汛、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至宿稀,卻和暖如春趁舀,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背祝沸。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工矮烹, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人罩锐。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓奉狈,卻偏偏與公主長得像,于是被迫代替她去往敵國和親唯欣。 傳聞我的和親對象是個殘疾皇子嘹吨,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內容