理解python異步機(jī)制

1. python yield與async/await

要點(diǎn)

最重要的是生成器函數(shù)碰到y(tǒng)ield停止執(zhí)行毫别,收到next或send才會(huì)繼續(xù)執(zhí)行的機(jī)制熄捍。
而且send方法令我們可以傳遞值到生成器暫停的地方。
生成器執(zhí)行結(jié)束拋出StopIteration 異常廓潜。
yield from用于把其他生成器當(dāng)做子例程調(diào)用抵皱。

MUST: yield from + iterable
MUST: @asyncio.coroutines + yield from / yield
MUST: await + coroutine / object with await() method (that must ruturn a iterable instead of a coroutine)
MUSTN'T: async def + yield/yield from

圖說

  • 以下兩張圖中是6組代碼,實(shí)現(xiàn)了同樣的控制程序執(zhí)行順序的功能辩蛋,調(diào)度器一樣呻畸,輸出也完全一樣,幾個(gè)小的演變一定程度上展示了yield和async/await的關(guān)系悼院。
由yield到async/await
由yield到async/await變化標(biāo)示
  • 下面這張圖是用python內(nèi)部的審查函數(shù)來判斷類型伤为,查看上圖中的@coroutine+yield構(gòu)成的函數(shù)到底在python內(nèi)部被當(dāng)做什么


    @coroutine+yield到底是什么

2. async/await 實(shí)驗(yàn)

  • 這是一系列關(guān)于await,async特性的小實(shí)驗(yàn)樱蛤,該新特性在PEP-0492中描述并在Python3.5中實(shí)現(xiàn)钮呀。是一個(gè)相當(dāng)不錯(cuò)的便于初學(xué)者理解await剑鞍,async的資源昨凡。
  • 在這里我翻譯了作者的綱要并闡述了自己的理解,不當(dāng)之處歡迎指正蚁署。
  • 代碼參看github/awaitexp便脊。

實(shí)驗(yàn)一:最簡調(diào)度器

  • 目的是有一個(gè)基本上最簡單可實(shí)現(xiàn)的協(xié)程調(diào)度器。它與標(biāo)準(zhǔn)庫中asyncio模塊中相對(duì)復(fù)雜的調(diào)度器并列光戈。

  • 首先創(chuàng)建一個(gè)最簡單的基于生成器的協(xié)程函數(shù)A

@types.coroutine
def switch():
    yield

然后它可以被其他用async def定義的的協(xié)程函數(shù)B和C await哪痰,只有當(dāng)await返回時(shí),B和C才繼續(xù)執(zhí)行久妆。
這樣我們就可以有效地控制B和C的執(zhí)行順序晌杰。
然后我們創(chuàng)建了一個(gè)調(diào)度器,它對(duì)列表進(jìn)行了兩次深拷貝以避免問題筷弦。它循環(huán)協(xié)程隊(duì)列肋演,使用send方法對(duì)每個(gè)協(xié)程依次遞進(jìn)抑诸,如果有協(xié)程已經(jīng)完成則將其移出隊(duì)列,當(dāng)列表中的協(xié)程全部完成時(shí)結(jié)束爹殊。

實(shí)驗(yàn)二:具有睡眠功能的簡單調(diào)度器

  • 目的是給我們的調(diào)度器添加一個(gè)awaitable的睡眠協(xié)程函數(shù)蜕乡。盡管睡眠功能本身是相當(dāng)簡單的,這還是給調(diào)度器增加了一定的復(fù)雜度梗夸。之后該調(diào)度器的睡眠功能是否能簡易地與其他事件組合將是一個(gè)有趣的問題层玲。

  • 我們?cè)诘谝粋€(gè)實(shí)驗(yàn)的基礎(chǔ)上首先修改switch函數(shù)使其yield返回輸入給它的參數(shù)的字典,并且添加一個(gè)新的調(diào)用它的協(xié)程函數(shù)反症。

async def sleep(delay):
    await switch(delay=delay)

然后通過args=coro.send(None)與該函數(shù)碰撞辛块,得到含有delay參數(shù)的字典作為send的返回值。便可以判斷出是否調(diào)用調(diào)度器的睡眠機(jī)制铅碍。
最后在調(diào)度器中實(shí)現(xiàn)每一次協(xié)程列表循環(huán)結(jié)束后判斷在睡眠列表中的協(xié)程是否有到時(shí)間的憨降,到時(shí)間或時(shí)間超出則添加到運(yùn)行協(xié)程列表中進(jìn)入循環(huán)執(zhí)行。如果運(yùn)行列表中的協(xié)程都執(zhí)行完了该酗,則查看睡眠列表中的協(xié)程中還需睡眠的最少時(shí)間授药,線程睡眠,睡眠完成再將其添加到運(yùn)行隊(duì)列呜魄。

實(shí)驗(yàn)三:使能增加新的協(xié)程

  • 當(dāng)前只有在開始指定的協(xié)程能被調(diào)度悔叽。該實(shí)驗(yàn)的目的是允許新的協(xié)程能夠在調(diào)度器開始運(yùn)行后被加入。

  • 在當(dāng)前實(shí)驗(yàn)中我們拓展調(diào)度器為一個(gè)類并持有自己的協(xié)程隊(duì)列爵嗅,并提供一個(gè)添加協(xié)程進(jìn)入隊(duì)列的方法娇澎。然后獲取調(diào)度器的唯一實(shí)例,在定義的協(xié)程中使用實(shí)例方法來加入新的協(xié)程睹晒,再把該協(xié)程加入調(diào)度趟庄,這樣就實(shí)現(xiàn)了在運(yùn)行中的某一刻加入新的協(xié)程。

實(shí)驗(yàn)四:和線程池進(jìn)行交互

  • 有時(shí)我們會(huì)有一些計(jì)算(或者I/O)任務(wù)伪很,它們最好能在背景中執(zhí)行戚啥,而不是在主線程中。這個(gè)實(shí)驗(yàn)展示了一個(gè)示例锉试,通過線程池執(zhí)行器和被調(diào)度的協(xié)程交互來獲得背景計(jì)算猫十。從長遠(yuǎn)看,這個(gè)技術(shù)很可能不是一個(gè)良好的基礎(chǔ)呆盖,因?yàn)樗荒芡瑫r(shí)伺服IO選擇器和線程隊(duì)列拖云。

  • 在這一歩實(shí)驗(yàn)中我們主要是添加了兩個(gè)部分,第一部分是一個(gè)裝飾器:

def background(fn):  
    @wraps(fn)
    async def wrapper(*args,**kwargs):
        return await switch(op='background',fn=fn,args=args,kwargs=kwargs)
    return wrapper

該裝飾器能將一個(gè)比較耗時(shí)的計(jì)算函數(shù)封裝為一個(gè)協(xié)程应又,使其可以被其他協(xié)程await宙项。在調(diào)度器中利用send函數(shù)的返回值可以獲取它的類型為background、函數(shù)入口地址以及函數(shù)的傳參株扛,然后在調(diào)度器中按相應(yīng)機(jī)制執(zhí)行尤筐。
第二部分是在調(diào)度器中的修改:我們讓調(diào)度器類擁有了一個(gè)私有的concurrent.futures.ThreadPoolExecutor()對(duì)象邑贴。并在運(yùn)行協(xié)程隊(duì)列的循環(huán)判斷中將background類型的操作提交給線程池對(duì)象,并將當(dāng)前的協(xié)程移出運(yùn)行隊(duì)列叔磷,添加到futures隊(duì)列中拢驾。然后在每次運(yùn)行隊(duì)列循環(huán)后判斷futures中的任務(wù)是否有完成的(使用的參數(shù)為一旦有任一任務(wù)完成或被取消都返回),如果主線程此時(shí)處于將要睡眠的狀態(tài)改基,就等待相應(yīng)的時(shí)間繁疤,沒有的話則立刻返回,下次再查詢秕狰,完成的任務(wù)將其所在協(xié)程帶入運(yùn)行隊(duì)列稠腊,任務(wù)結(jié)果通過調(diào)度器send傳回該協(xié)程。

實(shí)驗(yàn)五:

  • 協(xié)程的一個(gè)典型應(yīng)用就是和異步I/O交互鸣哀。該實(shí)驗(yàn)的目的是讓調(diào)度器和Python的selector模型進(jìn)行交互并提供一個(gè)基本的I/O模型架忌。

  • 類似于實(shí)驗(yàn)四,我們?cè)趯?shí)驗(yàn)三的基礎(chǔ)上讓調(diào)度器擁有了一個(gè)私有的selectors.DefaultSelector()對(duì)象我衬。創(chuàng)建了一個(gè)名為io輔助協(xié)程叹放,供其他協(xié)程調(diào)用并且給調(diào)度器提供類型和操作信息。同樣利用主線程睡眠時(shí)間來等待selectorselect方法挠羔。關(guān)鍵語句:for key, events in self.selector.select(timeout=timeout): ...井仰,select返回在超時(shí)時(shí)間內(nèi)IO準(zhǔn)備好的對(duì)象列表。
    此外一部分是創(chuàng)建服務(wù)端和客戶端的協(xié)程破加。
    服務(wù)端server協(xié)程函數(shù)中首先初始化socket俱恶,然后在主循環(huán)中await io操作(服務(wù)器本身是一個(gè)協(xié)程!)范舀,利用io輔助協(xié)程來等待io資源合是,取得連接后調(diào)用echo函數(shù)處理該連接,并將其加入調(diào)度器锭环。在echo協(xié)程中聪全,同樣利用io輔助協(xié)程異步獲取讀寫權(quán)限。循環(huán)直到break結(jié)束關(guān)閉鏈接田藐。
    對(duì)于客戶端測(cè)試程序荔烧,需要另外獲取一個(gè)調(diào)度器。定義一個(gè)echoclient協(xié)程函數(shù)汽久,利用io輔助協(xié)程異步發(fā)送接收信息。最后將所有測(cè)試協(xié)程加入調(diào)度器列表踊餐,開始執(zhí)行景醇。

部分結(jié)果,可以看出異步執(zhí)行的效果

實(shí)驗(yàn)六:

  • 目的是有效地合并實(shí)驗(yàn)四和實(shí)驗(yàn)五的特性吝岭。這將使用標(biāo)準(zhǔn)的self-pipe(實(shí)際上是一個(gè)self-socket)技巧來通知背景中復(fù)雜計(jì)算方法的完成三痰。

  • 在服務(wù)端的echo協(xié)程函數(shù)中添加了await add(3, 5)語句吧寺,add函數(shù)中添加sleep代表耗時(shí)的背景計(jì)算。在調(diào)度器類中添加了自己的一對(duì)socket散劫。當(dāng)有復(fù)雜計(jì)算時(shí)稚机,添加入futures隊(duì)列,同時(shí)設(shè)置future完成回調(diào)函數(shù)获搏,該回調(diào)函數(shù)向自己的socket發(fā)送提示完成的消息赖条。在select方法時(shí)判斷是否有準(zhǔn)備好的io描述符屬于自己擁有的socket,如果有就意味著一個(gè)計(jì)算的完成常熙,然后清空socket緩存纬乍,移除相應(yīng)的futures

程序結(jié)構(gòu)示意圖

相關(guān)知識(shí)點(diǎn)

  • selectors – High-level I/O multiplexing 基于select模塊原語
  • file object - File object指一個(gè)對(duì)于底層資源暴露了面向文件API(擁有read和write方法)的對(duì)象裸卫。一個(gè)文件對(duì)象會(huì)被調(diào)制到訪問一個(gè)真實(shí)的磁盤文件或者其他類型的存儲(chǔ)或通信設(shè)備仿贬。它也被叫做類文件對(duì)象或者流。實(shí)際上有三類文件對(duì)象:原生二進(jìn)制文件墓贿,緩沖二進(jìn)制文件以及文本文件茧泪。它們的接口被定義在io模塊中。創(chuàng)建一個(gè)文件對(duì)象的典型方法是使用open方法聋袋。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末调炬,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子舱馅,更是在濱河造成了極大的恐慌缰泡,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,029評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件代嗤,死亡現(xiàn)場(chǎng)離奇詭異棘钞,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)干毅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,395評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門宜猜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人硝逢,你說我怎么就攤上這事姨拥。” “怎么了渠鸽?”我有些...
    開封第一講書人閱讀 157,570評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵叫乌,是天一觀的道長。 經(jīng)常有香客問我徽缚,道長憨奸,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,535評(píng)論 1 284
  • 正文 為了忘掉前任凿试,我火速辦了婚禮排宰,結(jié)果婚禮上似芝,老公的妹妹穿的比我還像新娘。我一直安慰自己板甘,他們只是感情好党瓮,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,650評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著盐类,像睡著了一般寞奸。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上傲醉,一...
    開封第一講書人閱讀 49,850評(píng)論 1 290
  • 那天蝇闭,我揣著相機(jī)與錄音,去河邊找鬼硬毕。 笑死呻引,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的吐咳。 我是一名探鬼主播逻悠,決...
    沈念sama閱讀 39,006評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼韭脊!你這毒婦竟也來了童谒?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,747評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤沪羔,失蹤者是張志新(化名)和其女友劉穎饥伊,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蔫饰,經(jīng)...
    沈念sama閱讀 44,207評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡琅豆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,536評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了篓吁。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片茫因。...
    茶點(diǎn)故事閱讀 38,683評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖杖剪,靈堂內(nèi)的尸體忽然破棺而出冻押,到底是詐尸還是另有隱情,我是刑警寧澤盛嘿,帶...
    沈念sama閱讀 34,342評(píng)論 4 330
  • 正文 年R本政府宣布洛巢,位于F島的核電站,受9級(jí)特大地震影響孩擂,放射性物質(zhì)發(fā)生泄漏狼渊。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,964評(píng)論 3 315
  • 文/蒙蒙 一类垦、第九天 我趴在偏房一處隱蔽的房頂上張望狈邑。 院中可真熱鬧,春花似錦蚤认、人聲如沸米苹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,772評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蘸嘶。三九已至,卻和暖如春陪汽,著一層夾襖步出監(jiān)牢的瞬間训唱,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,004評(píng)論 1 266
  • 我被黑心中介騙來泰國打工挚冤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留况增,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,401評(píng)論 2 360
  • 正文 我出身青樓训挡,卻偏偏與公主長得像澳骤,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子澜薄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,566評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容

  • 前言 很多朋友對(duì)異步編程都處于“聽說很強(qiáng)大”的認(rèn)知狀態(tài)为肮。鮮有在生產(chǎn)項(xiàng)目中使用它。而使用它的同學(xué)肤京,則大多數(shù)都停留在知...
    星星在線閱讀 2,855評(píng)論 2 39
  • 1 什么是異步編程 通過學(xué)習(xí)相關(guān)概念颊艳,我們逐步解釋異步編程是什么。 1.1 阻塞 程序未得到所需計(jì)算資源時(shí)被掛起的...
    hugoren閱讀 2,652評(píng)論 2 10
  • 輕量級(jí)線程:協(xié)程 在常用的并發(fā)模型中,多進(jìn)程饭庞、多線程戒悠、分布式是最普遍的,不過近些年來逐漸有一些語言以first-c...
    Tenderness4閱讀 6,357評(píng)論 2 10
  • 冰破熏風(fēng)舟山, 河凌動(dòng)绸狐, 柳梢染綠。 舒望眼累盗, 縱橫溝壑寒矿, 飛沙遮目。 走馬平遙參圣母若债, 逡巡恒岳臨飛瀑符相。 更追仰, ...
    萬峰智叟閱讀 220評(píng)論 0 1
  • 自從生了娃之后,你的身份真是空前膨脹啊终;既要當(dāng)?shù)昧藡尵当瑩纹鹨粋€(gè)娃的各種作,還要當(dāng)?shù)昧似拮永渡颜煞蚩醋鲆粋€(gè)男人趟脂,同時(shí)做...
    2月曉閱讀 199評(píng)論 0 0