python从yield到asyncio<第五章>

承接python从yield到asyncio<第四章>中提到的代码问题。稍微修改一下代码

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

# 读取当当网的图书
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. 增加了多线程的下载函数thread_download_one, 和asyncio的方式一样在http请求的时候阻塞1s
  2. 承接我们上一章的问题, 上一章的问题主要就是在save_image()函数, save_image操作硬盘保存文件, 控制权交还给主循环, 此刻有很多子生成器都返回了数据等待主线程的处理, 会导致主线程阻塞, 我们模拟耗时操作硬盘(休眠0.5s), 最终耗时8.63s, 而多线程耗时6.63s左右, asyncio比多线程效率更低了, 线程池多个线程并发的写硬盘, 而此刻asyncio需要主线程处理完一个任务的写硬盘操作之后才能处理下一个任务, 所以效率会很低。

知道了问题所在, 下一步要做的是改写写硬盘的操作, 这个操作不能阻塞主线程, asyncio也为我们提供了这样的api, run_in_executor(), 该函数内部维护的是ThreadPoolExecutor线程池, 使用多线程的方式实现异步操作。

只需要改一下download_one函数

@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)

再次执行一下看一下运行时间。我执行1.3s, 相比于8.63s好了不少

补充:

  1. download_one函数中创建的loop循环对象和main函数中的loop对象是同一个, 可以看看源码或者id()一下
  2. 主函数中不要loop.close(), run_in_executor函数每次都会调用self._check_closed()检测循环是否关闭
    3.书本中还介绍了yield from semaphore来限制并发请求数量, 由于asyncio不向多线程那样阻塞, 加入循环事件任务被快速驱动, 并发访问人家的网页, 所以使用semaphore来及限制并发的数量, 让你的程序温柔对待他人的网站。这一块可以结合书中的代码学习, 这里不展开
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容

  • Object C中创建线程的方法是什么?如果在主线程中执行代码,方法是什么?如果想延时执行代码、方法又是什么? 1...
    AlanGe阅读 1,714评论 0 17
  • 注:本文很多素材来源于网络上前人总结和《流畅的python》一书,本人仅仅以个人视角重新整合,便于自己理解,再此声...
    第八共同体阅读 5,592评论 0 4
  • 本文是17年写的,至今过去多年,有一篇更好的文档: https://superfastpython.com/pyt...
    人世间阅读 104,240评论 51 234
  • 太爱一个人,我们就会变得多疑、猜忌、敏感;变态的恼怒、莫名的不安和焦虑,还有不着边际的自我幻想。所以,如果太爱一个...
    我麋鹿啦阅读 197评论 0 1
  • 朋友将我送至火车站,我们便分别了。我赶赴A城开报告会,在闷热的夏季里搭乘火车,往返两城之间赶会场,真不算是美差。握...
    正好周沫阅读 261评论 2 0