asyncio
在Python 2的時代,高性能的網(wǎng)絡(luò)編程主要是使用Twisted、Tornado和Gevent這三個庫,但是它們的異步代碼相互之間既不兼容也不能移植。如上一節(jié)說的,Gvanrossum希望在Python 3 實現(xiàn)一個原生的基于生成器的協(xié)程庫,其中直接內(nèi)置了對異步IO的支持,這就是asyncio,它在Python 3.4被引入到標(biāo)準(zhǔn)庫。
asyncio 這個包使用事件循環(huán)驅(qū)動的協(xié)程實現(xiàn)并發(fā)。
asyncio 包在引入標(biāo)準(zhǔn)庫之前代號 “Tulip”(郁金香),所以在網(wǎng)上搜索資料時,會經(jīng)常看到這種花的名字。
什么是事件循環(huán)?
wiki 上說:事件循環(huán)是”一種等待程序分配事件或者消息的編程架構(gòu)“。基本上來說事件循環(huán)就是:”當(dāng)A發(fā)生時,執(zhí)行B"。或者用最簡單的例子來解釋這一概念就是每個瀏覽器中都存在的JavaScript事件循環(huán)。當(dāng)你點擊了某個東西(“當(dāng)A發(fā)生時”),這一點擊動作會發(fā)送給JavaScript的事件循環(huán),并檢查是否存在注冊過的onclick 回調(diào)來處理這一點擊(執(zhí)行B)。只要有注冊過的回調(diào)函數(shù)就會伴隨點擊動作的細(xì)節(jié)信息被執(zhí)行。事件循環(huán)被認(rèn)為是一種虛幻是因為它不停的手機事件并通過循環(huán)來發(fā)如何應(yīng)對這些事件。
對 Python 來說,用來提供事件循環(huán)的 asyncio 被加入標(biāo)準(zhǔn)庫中。asyncio 重點解決網(wǎng)絡(luò)服務(wù)中的問題,事件循環(huán)在這里將來自套接字(socket)的 I/O 已經(jīng)準(zhǔn)備好讀和/或?qū)懽鳛椤爱?dāng)A發(fā)生時”(通過selectors模塊)。除了 GUI 和 I/O,事件循環(huán)也經(jīng)常用于在別的線程或子進程中執(zhí)行代碼,并將事件循環(huán)作為調(diào)節(jié)機制(例如,合作式多任務(wù))。如果你恰好理解 Python 的 GIL,事件循環(huán)對于需要釋放 GIL 的地方很有用。
線程與協(xié)程
我們先看兩斷代碼,分別用 threading 模塊和asyncio 包實現(xiàn)的一段代碼。
# sinner_thread.pyimport threadingimport itertoolsimport timeimport sysclass Signal: # 這個類定義一個可變對象,用于從外部控制線程 go = Truedef spin(msg, signal): # 這個函數(shù)會在單獨的線程中運行,signal 參數(shù)是前邊定義的Signal類的實例 write, flush = sys.stdout.write, sys.stdout.flush for char in itertools.cycle('|/-//'): # itertools.cycle 函數(shù)從指定的序列中反復(fù)不斷地生成元素 status = char + ' ' + msg write(status) flush() write('/x08' * len(status)) # 使用退格符把光標(biāo)移回行首 time.sleep(.1) # 每 0.1 秒刷新一次 if not signal.go: # 如果 go屬性不是 True,退出循環(huán) break write(' ' * len(status) + '/x08' * len(status)) # 使用空格清除狀態(tài)消息,把光標(biāo)移回開頭def slow_function(): # 模擬耗時操作 # 假裝等待I/O一段時間 time.sleep(3) # 調(diào)用sleep 會阻塞主線程,這么做事為了釋放GIL,創(chuàng)建從屬線程 return 42def supervisor(): # 這個函數(shù)設(shè)置從屬線程,顯示線程對象,運行耗時計算,最后殺死進程 signal = Signal() spinner = threading.Thread(target=spin, args=('thinking!', signal)) print('spinner object:', spinner) # 顯示線程對象 輸出 spinner object: <Thread(Thread-1, initial)> spinner.start() # 啟動從屬進程 result = slow_function() # 運行slow_function 行數(shù),阻塞主線程。同時叢書線程以動畫形式旋轉(zhuǎn)指針 signal.go = False spinner.join() # 等待spinner 線程結(jié)束 return resultdef main(): result = supervisor() print('Answer', result)if __name__ == '__main__': main()
新聞熱點
疑難解答