async/await

from time import sleep, time


def demo1():
    """
    假設(shè)我們有三臺(tái)洗衣機(jī), 現(xiàn)在有三批衣服需要分別放到這三臺(tái)洗衣機(jī)里面洗.
    """
    
    def washing1():
        sleep(3)  # 第一臺(tái)洗衣機(jī), 需要洗3秒才能洗完 (只是打個(gè)比方)
        print('washer1 finished')  # 洗完的時(shí)候, 洗衣機(jī)會(huì)響一下, 告訴我們洗完了
    
    def washing2():
        sleep(2)
        print('washer2 finished')
    
    def washing3():
        sleep(5)
        print('washer3 finished')
    
    washing1()
    washing2()
    washing3()
    
    """
    這個(gè)還是很容易理解的, 運(yùn)行 demo1(), 那么需要10秒鐘才能把全部衣服洗完.
    沒(méi)錯(cuò), 大部分時(shí)間都花在挨個(gè)地等洗衣機(jī)上了.
    """


def demo2():
    """
    現(xiàn)在我們想要避免無(wú)謂的等待, 為了提高效率, 我們將使用 async.
    washing1/2/3() 本是 "普通函數(shù)", 現(xiàn)在我們用 async 把它們升級(jí)為 "異步函數(shù)".
    
    注: 一個(gè)異步的函數(shù), 有個(gè)更標(biāo)準(zhǔn)的稱呼, 我們叫它 "協(xié)程" (coroutine).
    """
    
    async def washing1():
        sleep(3)
        print('washer1 finished')
    
    async def washing2():
        sleep(2)
        print('washer2 finished')
    
    async def washing3():
        sleep(5)
        print('washer3 finished')
    
    washing1()
    washing2()
    washing3()
    
    """
    從正常人的理解來(lái)看, 我們現(xiàn)在有了異步函數(shù), 但是卻忘了定義應(yīng)該什么時(shí)候 "離開" 一臺(tái)洗衣
    機(jī), 去看看另一個(gè)... 這就會(huì)導(dǎo)致, 現(xiàn)在的情況是我們一邊看著第一臺(tái)洗衣機(jī), 一邊著急地想著
    "是不是該去開第二臺(tái)洗衣機(jī)了呢?" 但又不敢去 (只是打個(gè)比方), 最終還是花了10秒的時(shí)間才
    把衣服洗完.
    
    PS: 其實(shí) demo2() 是無(wú)法運(yùn)行的, Python 會(huì)直接警告你:
        RuntimeWarning: coroutine 'demo2.<locals>.washing1' was never awaited
        RuntimeWarning: coroutine 'demo2.<locals>.washing2' was never awaited
        RuntimeWarning: coroutine 'demo2.<locals>.washing3' was never awaited
    """


def demo3():
    """
    現(xiàn)在我們吸取了上次的教訓(xùn), 告訴自己洗衣服的過(guò)程是 "可等待的" (awaitable), 在它開始洗衣服
    的時(shí)候, 我們可以去弄?jiǎng)e的機(jī)器.
    """
    
    async def washing1():
        await sleep(3)  # 注意這里加入了 await
        print('washer1 finished')
    
    async def washing2():
        await sleep(2)
        print('washer2 finished')
    
    async def washing3():
        await sleep(5)
        print('washer3 finished')
    
    washing1()
    washing2()
    washing3()
    
    """
    嘗試運(yùn)行一下, 我們會(huì)發(fā)現(xiàn)還是會(huì)報(bào)錯(cuò) (報(bào)錯(cuò)內(nèi)容和 demo2 一樣). 這里我說(shuō)一下原因, 以及在
    demo4 中會(huì)給出一個(gè)最終答案:
        1. 第一個(gè)問(wèn)題是, await 后面必須跟一個(gè) awaitable 類型或者具有 __await__ 屬性的
        對(duì)象. 這個(gè) awaitable, 并不是我們認(rèn)為 sleep() 是 awaitable 就可以 await 了,
        常見的 awaitable 對(duì)象應(yīng)該是:
            await asyncio.sleep(3)  # asyncio 庫(kù)的 sleep() 機(jī)制與 time.sleep() 不
            # 同, 前者是 "假性睡眠", 后者是會(huì)導(dǎo)致線程阻塞的 "真性睡眠"
            await an_async_function()  # 一個(gè)異步的函數(shù), 也是可等待的對(duì)象
        以下是不可等待的:
            await time.sleep(3)
            x = await 'hello'  # <class 'str'> doesn't define '__await__'
            x = await 3 + 2  # <class 'int'> dosen't define '__await__'
            x = await None  # ...
            x = await a_sync_function()  # 普通的函數(shù), 是不可等待的
            
        2. 第二個(gè)問(wèn)題是, 如果我們要執(zhí)行異步函數(shù), 不能用這樣的調(diào)用方法:
            washing1()
            washing2()
            washing3()
        而應(yīng)該用 asyncio 庫(kù)中的事件循環(huán)機(jī)制來(lái)啟動(dòng) (具體見 demo4 講解).
    """


def demo4():
    """
    這是最終我們想要的實(shí)現(xiàn).
    """
    import asyncio  # 引入 asyncio 庫(kù)
    
    async def washing1():
        await asyncio.sleep(3)  # 使用 asyncio.sleep(), 它返回的是一個(gè)可等待的對(duì)象
        print('washer1 finished')
    
    async def washing2():
        await asyncio.sleep(2)
        print('washer2 finished')
    
    async def washing3():
        await asyncio.sleep(5)
        print('washer3 finished')
    
    """
    事件循環(huán)機(jī)制分為以下幾步驟:
        1. 創(chuàng)建一個(gè)事件循環(huán)
        2. 將異步函數(shù)加入事件隊(duì)列
        3. 執(zhí)行事件隊(duì)列, 直到最晚的一個(gè)事件被處理完畢后結(jié)束
        4. 最后建議用 close() 方法關(guān)閉事件循環(huán), 以徹底清理 loop 對(duì)象防止誤用
    """
    # 1. 創(chuàng)建一個(gè)事件循環(huán)
    loop = asyncio.get_event_loop()
    
    # 2. 將異步函數(shù)加入事件隊(duì)列
    tasks = [
        washing1(),
        washing2(),
        washing3(),
    ]
    
    # 3. 執(zhí)行事件隊(duì)列, 直到最晚的一個(gè)事件被處理完畢后結(jié)束
    loop.run_until_complete(asyncio.wait(tasks))
    """
    PS: 如果不滿意想要 "多洗幾遍", 可以多寫幾句:
        loop.run_until_complete(asyncio.wait(tasks))
        loop.run_until_complete(asyncio.wait(tasks))
        loop.run_until_complete(asyncio.wait(tasks))
        ...
    """
    
    # 4. 如果不再使用 loop, 建議養(yǎng)成良好關(guān)閉的習(xí)慣
    # (有點(diǎn)類似于文件讀寫結(jié)束時(shí)的 close() 操作)
    loop.close()
    
    """
    最終的打印效果:
        washer2 finished
        washer1 finished
        washer3 finished
        elapsed time = 5.126561641693115
            (畢竟切換線程也要有點(diǎn)耗時(shí)的)
        
    說(shuō)句題外話, 我看有的博主的加入事件隊(duì)列是這樣寫的:
        tasks = [
            loop.create_task(washing1()),
            loop.create_task(washing2()),
            loop.create_task(washing3()),
        ]
        運(yùn)行的效果是一樣的, 暫不清楚為什么他們這樣做.
    """


if __name__ == '__main__':
    # 為驗(yàn)證是否真的縮短了時(shí)間, 我們計(jì)個(gè)時(shí)
    start = time()
    
    # demo1()  # 需花費(fèi)10秒
    # demo2()  # 會(huì)報(bào)錯(cuò): RuntimeWarning: coroutine ... was never awaited
    # demo3()  # 會(huì)報(bào)錯(cuò): RuntimeWarning: coroutine ... was never awaited
    demo4()  # 需花費(fèi)5秒多一點(diǎn)點(diǎn)
    
    end = time()
    print('elapsed time = ' + str(end - start))

來(lái)源:
https://blog.csdn.net/Likianta/article/details/90123678

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市及志,隨后出現(xiàn)的幾起案子扇苞,更是在濱河造成了極大的恐慌富寿,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,744評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件舔哪,死亡現(xiàn)場(chǎng)離奇詭異穴豫,居然都是意外死亡渤滞,警方通過(guò)查閱死者的電腦和手機(jī)渔肩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門纸泡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人赖瞒,你說(shuō)我怎么就攤上這事≡榧伲” “怎么了栏饮?”我有些...
    開封第一講書人閱讀 163,105評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)磷仰。 經(jīng)常有香客問(wèn)我袍嬉,道長(zhǎng),這世上最難降的妖魔是什么灶平? 我笑而不...
    開封第一講書人閱讀 58,242評(píng)論 1 292
  • 正文 為了忘掉前任伺通,我火速辦了婚禮,結(jié)果婚禮上逢享,老公的妹妹穿的比我還像新娘罐监。我一直安慰自己,他們只是感情好瞒爬,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,269評(píng)論 6 389
  • 文/花漫 我一把揭開白布弓柱。 她就那樣靜靜地躺著沟堡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪矢空。 梳的紋絲不亂的頭發(fā)上航罗,一...
    開封第一講書人閱讀 51,215評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音屁药,去河邊找鬼粥血。 笑死,一個(gè)胖子當(dāng)著我的面吹牛酿箭,可吹牛的內(nèi)容都是我干的复亏。 我是一名探鬼主播,決...
    沈念sama閱讀 40,096評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼七问,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼蜓耻!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起械巡,我...
    開封第一講書人閱讀 38,939評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤刹淌,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后讥耗,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體有勾,經(jīng)...
    沈念sama閱讀 45,354評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,573評(píng)論 2 333
  • 正文 我和宋清朗相戀三年古程,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蔼卡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,745評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡挣磨,死狀恐怖雇逞,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情茁裙,我是刑警寧澤塘砸,帶...
    沈念sama閱讀 35,448評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站晤锥,受9級(jí)特大地震影響掉蔬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜矾瘾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,048評(píng)論 3 327
  • 文/蒙蒙 一女轿、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧壕翩,春花似錦蛉迹、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)赏僧。三九已至,卻和暖如春扭倾,著一層夾襖步出監(jiān)牢的瞬間淀零,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工膛壹, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留驾中,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,776評(píng)論 2 369
  • 正文 我出身青樓模聋,卻偏偏與公主長(zhǎng)得像肩民,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子链方,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,652評(píng)論 2 354

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