同步、異步(gevent,asyncio)、多線程(threading)效率對(duì)比

對(duì)比了三種情況下采集50個(gè)網(wǎng)頁(yè)所需時(shí)間,可以看出多線程在效率上是遠(yuǎn)高于gevent的。第一次測(cè)試的時(shí)候,沒有使用monkey這個(gè)補(bǔ)丁,socket是阻塞調(diào)用的,效率并沒有提升,因?yàn)檫€是同步運(yùn)行的,使用monkey補(bǔ)丁后,使socket變?yōu)閰f(xié)作運(yùn)行,效率大大提升。

Python的運(yùn)行環(huán)境允許我們?cè)谶\(yùn)行時(shí)修改大部分的對(duì)象,包括模塊,類甚至函數(shù)。 這是個(gè)一般說來令人驚奇的壞主意,因?yàn)樗鼊?chuàng)造了“隱式的副作用”,如果出現(xiàn)問題 它很多時(shí)候是極難調(diào)試的。雖然如此,在極端情況下當(dāng)一個(gè)庫(kù)需要修改Python本身 的基礎(chǔ)行為的時(shí)候,猴子補(bǔ)丁就派上用場(chǎng)了。在這種情況下,gevent能夠 修改標(biāo)準(zhǔn)庫(kù)里面大部分的阻塞式系統(tǒng)調(diào)用,包括socket、ssl、threading和 select等模塊,而變?yōu)閰f(xié)作式運(yùn)行。

例如,Redis的python綁定一般使用常規(guī)的tcp socket來與redis-server實(shí)例通信。 通過簡(jiǎn)單地調(diào)用gevent.monkey.patch_all(),可以使得redis的綁定協(xié)作式的調(diào)度 請(qǐng)求,與gevent棧的其它部分一起工作。

這讓我們可以將一般不能與gevent共同工作的庫(kù)結(jié)合起來,而不用寫哪怕一行代碼。 雖然猴子補(bǔ)丁仍然是邪惡的(evil),但在這種情況下它是“有用的邪惡(useful evil)”。

執(zhí)行的結(jié)果分別是

同步執(zhí)行:4.50s
gevent異步:0.47s
threading多線程:0.58s
更新asyncio: 1.1s
gevent并不是同時(shí)執(zhí)行,還是按順序執(zhí)行,并未打亂輸出結(jié)果,多線程不按順序執(zhí)行,打亂了輸出結(jié)果。網(wǎng)絡(luò)狀況好(IO操作延時(shí)低)的情況下,gevent能稍微提高點(diǎn)效率,IO操作很費(fèi)時(shí)的情況gevent效率將大大提高,效率最高的還是多線程。

gevent:當(dāng)一個(gè)greenlet遇到IO操作時(shí),比如訪問網(wǎng)絡(luò),就自動(dòng)切換到其他的greenlet,等到IO操作完成,再在適當(dāng)?shù)臅r(shí)候切換回來繼續(xù)執(zhí)行。由于IO操作非常耗時(shí),經(jīng)常使程序處于等待狀態(tài),有了gevent為我們自動(dòng)切換協(xié)程,就保證總有g(shù)reenlet在運(yùn)行,而不是等待IO。gevent - 廖雪峰的官方網(wǎng)站

# /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下查看頁(yè)面生成的時(shí)間

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

更新

增加python3.5下的asyncio測(cè)試,時(shí)間比gevent和threading長(zhǎng)一點(diǎn),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)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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