同步、异步(gevent,asyncio)、多线程(threading)效率对比

对比了三种情况下采集50个网页所需时间,可以看出多线程在效率上是远高于gevent的。第一次测试的时候,没有使用monkey这个补丁,socket是阻塞调用的,效率并没有提升,因为还是同步运行的,使用monkey补丁后,使socket变为协作运行,效率大大提升。

Python的运行环境允许我们在运行时修改大部分的对象,包括模块,类甚至函数。 这是个一般说来令人惊奇的坏主意,因为它创造了“隐式的副作用”,如果出现问题 它很多时候是极难调试的。虽然如此,在极端情况下当一个库需要修改Python本身 的基础行为的时候,猴子补丁就派上用场了。在这种情况下,gevent能够 修改标准库里面大部分的阻塞式系统调用,包括socket、ssl、threading和 select等模块,而变为协作式运行。

例如,Redis的python绑定一般使用常规的tcp socket来与redis-server实例通信。 通过简单地调用gevent.monkey.patch_all(),可以使得redis的绑定协作式的调度 请求,与gevent栈的其它部分一起工作。

这让我们可以将一般不能与gevent共同工作的库结合起来,而不用写哪怕一行代码。 虽然猴子补丁仍然是邪恶的(evil),但在这种情况下它是“有用的邪恶(useful evil)”。

执行的结果分别是

同步执行:4.50s
gevent异步:0.47s
threading多线程:0.58s
更新asyncio: 1.1s
gevent并不是同时执行,还是按顺序执行,并未打乱输出结果,多线程不按顺序执行,打乱了输出结果。网络状况好(IO操作延时低)的情况下,gevent能稍微提高点效率,IO操作很费时的情况gevent效率将大大提高,效率最高的还是多线程。

gevent:当一个greenlet遇到IO操作时,比如访问网络,就自动切换到其他的greenlet,等到IO操作完成,再在适当的时候切换回来继续执行。由于IO操作非常耗时,经常使程序处于等待状态,有了gevent为我们自动切换协程,就保证总有greenlet在运行,而不是等待IO。gevent - 廖雪峰的官方网站

# /usr/bin/python3
# -*- coding: utf-8 -*-
# Copyright (c) 2017 - walker <cail1844@gmail.com>

import gevent
import requests
import re
import timeit
import codecs
from threading import Thread
from gevent import monkey
monkey.patch_socket()


def get_title(url,title_list=[]):
    try:
        r = requests.get(url,timeout=5)
        r.encoding = 'utf8'
        html = r.text
        title = re.search(r'<title>(.*?)</title>',html).group(1)
    except TimeoutError:
        title = ''
    if title:
        title_list.append(title)
    return title

def get_url():
    baseurl = 'http://www.baidu.com/s?cl=3&tn=baidutop10&fr=top1000&wd=%s'
    f = codecs.open('muci.txt','r','utf8')
    url_list = []
    for key in f.readlines():
        url_list.append(baseurl % key.strip())
    return url_list


url_list = get_url()

def run1():
    title_list = []
    for url in url_list:
        get_title(url,title_list)
        # title_list.append(title)
    print('Sync result length:',len(title_list),title_list)
    return title_list

def run2():
    title_list = []
    threads = [gevent.spawn(get_title,url,title_list) for url in url_list]
    gevent.joinall(threads)
    # title_list = [thread.value for thread in threads]
    print('gevent result length:',len(title_list),title_list)
    return title_list

def run3():
    title_list = []
    th = []
    for url in url_list:
        t = Thread(target=get_title,args=(url,title_list))
        th.append(t)
        t.start()
    for t in th:
        t.join()
    print('threading result length:',len(title_list),title_list)
    return title_list


if __name__ == '__main__':
    t1 = timeit.timeit('run1()',setup="from __main__ import run1",number=1)
    print('sync time:',t1)
    t2 = timeit.timeit('run2()',setup="from __main__ import run2",number=1)
    print('gevent time:',t2)
    t3 = timeit.timeit('run3()',setup="from __main__ import run3",number=1)
    print('thread time:',t3)

放到django下查看页面生成的时间

同步:19065ms
gevent:1555ms
threading:1166ms

更新

增加python3.5下的asyncio测试,时间比gevent和threading长一点,1.1s

async def get_title2():
    loop = asyncio.get_event_loop()
    title_list = []
    io = []
    for url in url_list:
        # get_title(url, title_list)
        io.append(loop.run_in_executor(None,get_title,url,title_list))
    for i in io:
        await i
    print(threading.current_thread(), 'Sync result length:', len(title_list), title_list)
    return title_list

def run4():
    loop = asyncio.get_event_loop()
    loop.run_until_complete(get_title2())
if __name__ == '__main__':
    global url_list
    url_list = get_url()
    t4 = timeit.timeit('run4()', setup="from __main__ import run4", number=1)
    print('thread time:', t4)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,194评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,058评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,780评论 0 346
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,388评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,430评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,764评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,907评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,679评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,122评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,459评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,605评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,270评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,867评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,734评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,961评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,297评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,472评论 2 348

推荐阅读更多精彩内容