摘要:輔之以事件循環(huán),協(xié)程可用于異步處理,尤其是在中。當(dāng)前支持的協(xié)程基于增強(qiáng)型生成器,于版本開始采用。新的特性中,異步還有兩種新用途異步內(nèi)容管理器和迭代器。
現(xiàn)在 Python 已經(jīng)支持用協(xié)程進(jìn)行異步處理。但最近有建議稱添加協(xié)程以全面完善 Python 的語言結(jié)構(gòu),而不是像現(xiàn)在這樣把他們作為生成器的一個(gè)類型。此外,兩個(gè)新的關(guān)鍵字———異步(async)和等待(await),都該添加到 Python 中來支持協(xié)程。
也許有人不太了解協(xié)程,其實(shí)協(xié)程的原理很簡單,打個(gè)比方就能講明白了:假設(shè)有十個(gè)人去食堂打飯,這個(gè)食堂比較窮,只有一個(gè)打飯窗口和一個(gè)打飯阿姨,那么打飯就只能一個(gè)一個(gè)排隊(duì)進(jìn)行。這十個(gè)人胃口很大,每個(gè)人都要點(diǎn)5個(gè)菜,但這十個(gè)人都喜歡猶豫不決,點(diǎn)菜的時(shí)候每點(diǎn)一個(gè)菜后再想下一個(gè)菜點(diǎn)什么,因此后面的人等得很著急呀。
這樣一直站著也不是個(gè)事兒,所以打菜的阿姨看到某人猶豫5秒后就會(huì)吼一聲,讓他排到隊(duì)伍末尾,讓別人先打菜,等輪到他的時(shí)候他也差不多想好吃什么了。這確實(shí)是個(gè)不錯(cuò)的方法,但也有一個(gè)缺點(diǎn),那就是打菜的阿姨會(huì)等每個(gè)人5秒鐘,如果那個(gè)人在5秒內(nèi)沒有做出決定吃啥,其實(shí)這5秒就浪費(fèi)了。一個(gè)人點(diǎn)一個(gè)菜就是浪費(fèi)5秒,十個(gè)人每個(gè)人點(diǎn)5個(gè)菜可就浪費(fèi)的多啦「菜都涼了要」。
那怎么辦呢?阿姨又發(fā)話了:大家都是學(xué)生,學(xué)生就要自覺,我以后也不主動(dòng)讓你們排到末尾了,如果你們覺得自己會(huì)猶豫不決,就主動(dòng)點(diǎn)直接點(diǎn)一個(gè)菜就站后面去,等下次排到的時(shí)候也差不多想好吃啥了。
這個(gè)方法果然有效,大家點(diǎn)了菜后想的第一件事情不是下一個(gè)菜吃什么,而是自己會(huì)不會(huì)猶豫,如果會(huì)猶豫那直接排到隊(duì)伍后面去,如果不會(huì)就接著點(diǎn)菜。這樣一來整個(gè)隊(duì)伍的效率自然就高了。
這個(gè)例子里,排隊(duì)阿姨的那聲吼就是我們的 CPU 中斷,用于切換上下文。每個(gè)打飯的學(xué)生就是一個(gè) task。而每個(gè)人決定自己要不要讓出窗口的這種行為,其實(shí)就是我們協(xié)程的核心思想。
OK,回到主題,協(xié)程就是一種可以在代碼的各種預(yù)定義位置暫停和恢復(fù)執(zhí)行的函數(shù),它避免了無意義的調(diào)度,由此提高代碼性能。而子程序是一種特殊的協(xié)同程序,它只有單一入口,通過回調(diào)來完成執(zhí)行。Python 的協(xié)程「現(xiàn)有的以生成器為基礎(chǔ)的協(xié)程和新提出的協(xié)程」不是一般意義上的協(xié)程,因?yàn)樵趫?zhí)行暫停時(shí)它們只能將控制權(quán)轉(zhuǎn)給調(diào)用者,而不是像常見的那樣將控制權(quán)轉(zhuǎn)給別的協(xié)程。輔之以事件循環(huán),協(xié)程可用于異步處理,尤其是在 I / O 中。
Python 當(dāng)前支持的協(xié)程基于 PEP342 增強(qiáng)型生成器,于 Python 2.5 版本開始采用。該 PEP 將 yield 語句改為表達(dá)式,并為生成器增加了一些新的方法 「 send() , throw() , and close() 」 ,同時(shí)確保 close() 方法在生成器進(jìn)入垃圾回收階段時(shí)得到調(diào)用。該功能在 Python 3.3 版本的 PEP 380 中得到進(jìn)一步增強(qiáng),它通過增加 yield 表達(dá)式,允許生成器將部分功能授予另一個(gè)生成器「即子生成器」。
以上方法都使協(xié)程依賴于生成器,這使得在代碼段何處進(jìn)行異步調(diào)用變得令人困惑,且頗受限制。尤其,with 和 for 聲明在理論上可以將協(xié)程用于異步調(diào)用,但 Python 語法在那些位置不允許使用 yield 表達(dá)式,因此無法進(jìn)行異步調(diào)用。此外,如果協(xié)程的重構(gòu)將 yield 或 yield from 從函數(shù)中移除 ,它就不再被視為協(xié)程,這會(huì)導(dǎo)致一些不明顯的錯(cuò)誤; asyncio 模塊通過 @asyncio.coroutine 裝飾器來彌補(bǔ)這方面的不足。
PEP 492 旨在解決以上所有問題。其想法源于 Yury Selivanov 在四月中旬提出的 python-ideas 郵件列表,該想法受到很多人熱情追捧。在5月5日,Guido van Rossum 同意將它添加在 Python 3.5 版本中。不僅如此,5月12日就得到執(zhí)行。一切都進(jìn)展迅速,盡管最終該方法還是在 python-idea 和 python-dev 方面引起熱情討論。
從語法角度看,變化相當(dāng)簡單:
async def read_data(db): data = await db.fetch("SELECT ...") ...
這個(gè)例子「來源于 PEP」將使用新的 async def 構(gòu)造函數(shù)創(chuàng)建一個(gè) read_data() 協(xié)程。 await 表達(dá)式將暫停執(zhí)行 read_data(),直到 db.fetch() await able 完成并返回其結(jié)果。await 類似于 yeild from ,但它會(huì)確保其參數(shù) awaitable。
此外還有幾種不同類型的 awaitable。一種是本地的協(xié)程對(duì)象,在調(diào)用本地協(xié)同程序后的返回為 awaitable,還有基于生成器且有 @types.coroutine 裝飾的協(xié)程。還有一種是未來對(duì)象,它代表著在未來完成的操作,也是 awaitable。__await __()方法在 awaitable 的對(duì)象都會(huì)出現(xiàn)。
然而,向一種語言添加新的關(guān)鍵字時(shí)會(huì)出現(xiàn)這樣的問題:任何與關(guān)鍵字名字相同的變量都會(huì)成為語法錯(cuò)誤。為了避免該問題,Python 3.5 和 3.6 版本將 “softly deprecate “ 「溫柔棄用」 async 和 await 為變量名,而不將他們當(dāng)做語法錯(cuò)誤。解析器會(huì)跟蹤 async def 塊,并將塊內(nèi)的關(guān)鍵字區(qū)別對(duì)待,從而使現(xiàn)有的使用繼續(xù)有效。
新的特性中,異步還有兩種新用途:異步內(nèi)容管理器(with)和迭代器(for)。在協(xié)程里,這兩種構(gòu)造函數(shù)的示例如下:
async def commit(session, data): ... async with session.transaction(): ... await session.update(data) ... ... async for row in Cursor(): print(row)
異步內(nèi)容管理器必須實(shí)現(xiàn)兩個(gè)異步方法,__aenter __()和__aexit __(),他們都返回 awaitables;異步迭代器須實(shí)現(xiàn)__aiter __()和__anext __()。這些方法都是現(xiàn)有的同步內(nèi)容管理器和迭代器的異步版本。
此前主要的討論是延期執(zhí)行的 “cofunction” 功能 PEP 3152 是否會(huì)是更好的起點(diǎn),該 PEP 的作者 Greg Ewing 提出了此問題。但有很多人認(rèn)為 Selivanov 提議的語法更適合 codef,cocall ,也有人更加贊同 Ewing 的提議。這樣來來回回的爭論了很多次。有一些人認(rèn)為cofunction 的語法在處理某些情況時(shí)相當(dāng)復(fù)雜并且不符合 Python 語言的特性。后來 Van Rossum 總結(jié)了 cofunctions 語法存在的問題,并拒絕采納該方法。
此外,還有幾點(diǎn)關(guān)于附加異步功能的建議值得討論,但并不緊急。對(duì)于關(guān)鍵詞的討論有些本末倒置。 await 的優(yōu)先級(jí)問題也討論了一段時(shí)間,結(jié)果是,不同于 yeild 和 yeild from 僅有最低優(yōu)先級(jí),await 具有較高的優(yōu)先級(jí)。
但 Mark Shannon 抱怨說,實(shí)現(xiàn) Selivanov 的建議并不需要增加新的語法。其他人也提出了類似的意見,但 Selivanov 或其他支持者并未對(duì)此提出反駁。關(guān)鍵在于簡化協(xié)同程序的編寫。除此之外,Van Rossum 希望協(xié)同程序暫停的位置能夠顯而易見,查看代碼就能發(fā)現(xiàn):
新的語法才是 PEP 存在的意義。我希望通過句法結(jié)構(gòu)就能判斷出協(xié)程的懸停點(diǎn)。
在兩三周后,發(fā)布了多個(gè)版本的 PEP ,引起了諸多辯論。Selivanov 耐心地解釋他的想法,并根據(jù)反饋意見不斷修正自己的想法。異步協(xié)程特性對(duì) Python 語言的未來很可能至關(guān)重要,整個(gè)探索過程都很快,很順?biāo)?。不過,Python 開發(fā)者們將這些想法付諸實(shí)踐很可能還需要一段時(shí)間。
原文地址:Python coroutines with async and await
參考文章: 對(duì)Python中yield和協(xié)程的理解
本文系 OneAPM 工程師編譯整理。想閱讀更多技術(shù)文章,請(qǐng)?jiān)L問 OneAPM 官方博客。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/37579.html
摘要:創(chuàng)建第一個(gè)協(xié)程推薦使用語法來聲明協(xié)程,來編寫異步應(yīng)用程序。協(xié)程兩個(gè)緊密相關(guān)的概念是協(xié)程函數(shù)通過定義的函數(shù)協(xié)程對(duì)象調(diào)用協(xié)程函數(shù)返回的對(duì)象。它是一個(gè)低層級(jí)的可等待對(duì)象,表示一個(gè)異步操作的最終結(jié)果。 我們講以Python 3.7 上的asyncio為例講解如何使用Python的異步IO。 showImg(https://segmentfault.com/img/remote/14600000...
摘要:所以與多線程相比,線程的數(shù)量越多,協(xié)程性能的優(yōu)勢(shì)越明顯。值得一提的是,在此過程中,只有一個(gè)線程在執(zhí)行,因此這與多線程的概念是不一樣的。 真正有知識(shí)的人的成長過程,就像麥穗的成長過程:麥穗空的時(shí)候,麥子長得很快,麥穗驕傲地高高昂起,但是,麥穗成熟飽滿時(shí),它們開始謙虛,垂下麥芒。 ——蒙田《蒙田隨筆全集》 上篇論述了關(guān)于python多線程是否是雞肋的問題,得到了一些網(wǎng)友的認(rèn)可,當(dāng)然也有...
摘要:上一篇文章第一章異步及協(xié)程基礎(chǔ)第二節(jié)關(guān)鍵字下一篇文章第二章實(shí)戰(zhàn)演練開發(fā)網(wǎng)站第一節(jié)網(wǎng)站結(jié)構(gòu)使用協(xié)程可以開發(fā)出類似同步代碼的異步行為。協(xié)程函數(shù)可以通過以下三張方式調(diào)用在本身是協(xié)程的函數(shù)內(nèi)通過關(guān)鍵字調(diào)用。 上一篇文章:Python:Tornado 第一章:異步及協(xié)程基礎(chǔ):第二節(jié):Python關(guān)鍵字yield下一篇文章:Python:Tornado 第二章:實(shí)戰(zhàn)演練:開發(fā)Tornado網(wǎng)站:第...
摘要:事件循環(huán)是異步編程的底層基石。對(duì)事件集合進(jìn)行輪詢,調(diào)用回調(diào)函數(shù)等一輪事件循環(huán)結(jié)束,循環(huán)往復(fù)。協(xié)程直接利用代碼的執(zhí)行位置來表示狀態(tài),而回調(diào)則是維護(hù)了一堆數(shù)據(jù)結(jié)構(gòu)來處理狀態(tài)。時(shí)代的協(xié)程技術(shù)主要是,另一個(gè)比較小眾。 Coding Crush Python開發(fā)工程師 主要負(fù)責(zé)豈安科技業(yè)務(wù)風(fēng)險(xiǎn)情報(bào)系統(tǒng)redq。 引言 1.1. 存儲(chǔ)器山 存儲(chǔ)器山是 Randal Bryant 在《深入...
摘要:具有以下基本同步原語子進(jìn)程提供了通過創(chuàng)建和管理子進(jìn)程的。雖然隊(duì)列不是線程安全的,但它們被設(shè)計(jì)為專門用于代碼。表示異步操作的最終結(jié)果。 Python的asyncio是使用 async/await 語法編寫并發(fā)代碼的標(biāo)準(zhǔn)庫。通過上一節(jié)的講解,我們了解了它不斷變化的發(fā)展歷史。到了Python最新穩(wěn)定版 3.7 這個(gè)版本,asyncio又做了比較大的調(diào)整,把這個(gè)庫的API分為了 高層級(jí)API和...
閱讀 4953·2023-04-25 18:47
閱讀 2684·2021-11-19 11:33
閱讀 3455·2021-11-11 16:54
閱讀 3109·2021-10-26 09:50
閱讀 2554·2021-10-14 09:43
閱讀 678·2021-09-03 10:47
閱讀 681·2019-08-30 15:54
閱讀 1508·2019-08-30 15:44