python從yield到asyncio<第五章>

承接python從yield到asyncio<第四章>中提到的代碼問題。稍微修改一下代碼

# -*- coding: utf-8 -*-

# 讀取當當網(wǎng)的圖書
import requests
import aiohttp
import asyncio
from bs4 import BeautifulSoup
import time
import os
from concurrent.futures import ThreadPoolExecutor


# 子生成器
@asyncio.coroutine
def get_image(img_url):
    yield from asyncio.sleep(1)
    resp = yield from aiohttp.request('GET', img_url)
    image = yield from resp.read()
    return image


def save_image(img, img_url):
    time.sleep(0.5)
    with open(os.path.join('./img_file', img_url.split('/')[-1]), 'wb') as f:
        f.write(img)


@asyncio.coroutine
def download_one(img_url):
    image = yield from get_image(img_url)
    save_image(image, img_url)


def thread_download_one(img_url):
    time.sleep(1)
    resp = requests.get(img_url)
    image = resp.text
    save_image(image, img_url)

if __name__ == '__main__':
    images_list = [
        'http://img3m0.ddimg.cn/67/4/24003310-1_b_5.jpg'
        'http://img3m2.ddimg.cn/43/13/23958142-1_b_12.jpg',
        'http://img3m0.ddimg.cn/60/17/24042210-1_b_5.jpg',
        'http://img3m4.ddimg.cn/20/11/23473514-1_b_5.jpg',
        'http://img3m4.ddimg.cn/40/14/22783504-1_b_1.jpg',
        'http://img3m7.ddimg.cn/43/25/23254747-1_b_3.jpg',
        'http://img3m9.ddimg.cn/30/36/23368089-1_b_2.jpg',
        'http://img3m1.ddimg.cn/77/14/23259731-1_b_0.jpg',
        'http://img3m2.ddimg.cn/33/18/23321562-1_b_21.jpg',
        'http://img3m3.ddimg.cn/2/21/22628333-1_b_2.jpg',
        'http://img3m8.ddimg.cn/85/30/23961748-1_b_10.jpg',
        'http://img3m1.ddimg.cn/90/34/22880871-1_b_3.jpg',
        'http://img3m2.ddimg.cn/62/27/23964002-1_b_6.jpg',
        'http://img3m5.ddimg.cn/84/16/24188655-1_b_3.jpg',
        'http://img3m6.ddimg.cn/46/1/24144166-1_b_23081.jpg',
        'http://img3m9.ddimg.cn/79/8/8766529-1_b_0.jpg']
    start = time.time()
    loop = asyncio.get_event_loop()
    to_do_tasks = [download_one(img) for img in images_list]

    res, _ = loop.run_until_complete(asyncio.wait(to_do_tasks))
    print(len(res))
    print('asyncio cost:' + str(time.time() - start))
    # ======================多線程版本===============================
    start = time.time()
    with ThreadPoolExecutor() as executor:
        res = [executor.submit(thread_download_one, i) for i in images_list]
    print(len(res))
    print('Thread cost:' + time.time() - start)
代碼解讀
  1. 增加了多線程的下載函數(shù)thread_download_one, 和asyncio的方式一樣在http請求的時候阻塞1s
  2. 承接我們上一章的問題, 上一章的問題主要就是在save_image()函數(shù), save_image操作硬盤保存文件, 控制權(quán)交還給主循環(huán), 此刻有很多子生成器都返回了數(shù)據(jù)等待主線程的處理, 會導(dǎo)致主線程阻塞, 我們模擬耗時操作硬盤(休眠0.5s), 最終耗時8.63s, 而多線程耗時6.63s左右, asyncio比多線程效率更低了, 線程池多個線程并發(fā)的寫硬盤, 而此刻asyncio需要主線程處理完一個任務(wù)的寫硬盤操作之后才能處理下一個任務(wù), 所以效率會很低。

知道了問題所在, 下一步要做的是改寫寫硬盤的操作, 這個操作不能阻塞主線程, asyncio也為我們提供了這樣的api, run_in_executor(), 該函數(shù)內(nèi)部維護的是ThreadPoolExecutor線程池, 使用多線程的方式實現(xiàn)異步操作。

只需要改一下download_one函數(shù)

@asyncio.coroutine
def download_one(img_url):
    image = yield from get_image(img_url)
    loop = asyncio.get_event_loop()
    loop.run_in_executor(None, save_image, image, img_url)

再次執(zhí)行一下看一下運行時間。我執(zhí)行1.3s, 相比于8.63s好了不少

補充:

  1. download_one函數(shù)中創(chuàng)建的loop循環(huán)對象和main函數(shù)中的loop對象是同一個, 可以看看源碼或者id()一下
  2. 主函數(shù)中不要loop.close(), run_in_executor函數(shù)每次都會調(diào)用self._check_closed()檢測循環(huán)是否關(guān)閉
    3.書本中還介紹了yield from semaphore來限制并發(fā)請求數(shù)量, 由于asyncio不向多線程那樣阻塞, 加入循環(huán)事件任務(wù)被快速驅(qū)動, 并發(fā)訪問人家的網(wǎng)頁, 所以使用semaphore來及限制并發(fā)的數(shù)量, 讓你的程序溫柔對待他人的網(wǎng)站。這一塊可以結(jié)合書中的代碼學習, 這里不展開
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Object C中創(chuàng)建線程的方法是什么?如果在主線程中執(zhí)行代碼,方法是什么?如果想延時執(zhí)行代碼、方法又是什么? 1...
    AlanGe閱讀 1,908評論 0 17
  • 注:本文很多素材來源于網(wǎng)絡(luò)上前人總結(jié)和《流暢的python》一書,本人僅僅以個人視角重新整合,便于自己理解,再此聲...
    第八共同體閱讀 5,710評論 0 4
  • 本文是17年寫的,至今過去多年,有一篇更好的文檔: https://superfastpython.com/pyt...
    人世間閱讀 105,249評論 51 234
  • 太愛一個人,我們就會變得多疑、猜忌、敏感;變態(tài)的惱怒、莫名的不安和焦慮,還有不著邊際的自我幻想。所以,如果太愛一個...
    我麋鹿啦閱讀 213評論 0 1
  • 朋友將我送至火車站,我們便分別了。我趕赴A城開報告會,在悶熱的夏季里搭乘火車,往返兩城之間趕會場,真不算是美差。握...
    正好周沫閱讀 329評論 2 0

友情鏈接更多精彩內(nèi)容