gevent:異步理論與實戰

gevent庫中使用的最核心的是Greenlet-一種用C寫的輕量級python模塊。在任意時間,系統只能容許一個Greenlet處於運行狀態。那怎麼讓程序高併發,從而實現程序高效運行呢?
html

這就是咱們常說的異步,在網絡請求中,能夠用下面的圖清晰的看出異步的效率python

串行和異步

高併發的核心是讓一個大的任務分紅一批子任務,而且子任務會被被系統高效率的調度,實現同步或者異步。在兩個子任務之間切換,也就是常常說到的上下文切換。web

同步就是讓子任務串行,而異步有點影分身之術,但在任意時間點,真身只有一個,子任務並非真正的並行,而是充分利用了碎片化的時間,讓程序不要浪費在等待上。這就是異步,效率槓桿的。算法

gevent中的上下文切換是經過yield實現。在這個例子中,咱們會有兩個子任務,互相利用對方等待的時間作本身的事情。這裏咱們使用gevent.sleep(0)表明程序會在這裏停0秒。數據庫

import gevent

def foo():    print('Running in foo')    gevent.sleep(0)    print('Explicit context switch to foo again')

def bar():    print('Explicit context to bar')    gevent.sleep(0)    print('Implicit context switch back to bar')

gevent.joinall([    gevent.spawn(foo),    gevent.spawn(bar),])

我谷歌了一下,spawn的意思是分支,這就很好的跟上面的那個圖對應起來,增強記憶。spawn-影分身之術。O(∩_∩)O~,讓待運行的任務切分紅更小的一批子任務。微信

下面咱們看看運行的順序:網絡

Running in foo
Explicit context to bar
Explicit context switch to foo again
Implicit context switch back to bar

這裏我放一個動圖,看看整個大的任務的調度順序併發

同步異步的順序問題

同步運行就是串行,123456...,可是異步的順序是隨機的任意的(根據子任務消耗的時間而定)。echarts

下面咱們來看個代碼dom

import gevent
import random

def task(pid):    """    Some non-deterministic task    """    gevent.sleep(random.randint(0,2)*0.001)    print('Task %s done' % pid)

#同步(結果更像串行)
def synchronous():    for i in range(1,10):        task(i)

#異步(結果更像亂步)
def asynchronous():    threads = [gevent.spawn(task, i) for i in range(10)]    gevent.joinall(threads)

print('Synchronous同步:')
synchronous()

print('Asynchronous異步:')
asynchronous()
Synchronous同步:
Task 1 done
Task 2 done
Task 3 done
Task 4 done
Task 5 done
Task 6 done
Task 7 done
Task 8 done
Task 9 done
Asynchronous異步:
Task 1 done
Task 5 done
Task 6 done
Task 2 done
Task 4 done
Task 7 done
Task 8 done
Task 9 done
Task 0 done
Task 3 done

同步案例中全部的任務都是按照順序執行,這致使主程序是阻塞式的(阻塞會暫停主程序的執行)。

gevent.spawn會對傳入的任務(子任務集合)進行進行調度,gevent.joinall方法會阻塞當前程序,除非全部的greenlet都執行完畢,程序纔會結束。

實戰

gevent以前寫過一期,但只是比較效率。這一期咱們要實現gevent到底怎麼用,怎麼把異步訪問獲得的數據提取出來。

最近作了個英語文本數據處理的任務,先作詞頻統計,而後對每一個詞語標註音標和註釋。其中標註音標和註釋,我沒有詞典,只能用爬蟲的方式訪問有道詞典,獲取想要的數據。

可是常規的for循環,word by word很慢,因而就想到用gevent。

分析url規律

首先抓包分析,打開開發者工具,清空訪問記錄。在有道詞典搜索框輸入「hello」按回車。觀察數據請求狀況 發現有道的url構建很簡單。

#url構建只須要傳入word便可
url = "http://dict.youdao.com/w/eng/{}/".format(word)

解析網頁數據

def fetch_word_info(word):

    url = "http://dict.youdao.com/w/eng/{}/".format(word)
    
    resp = requests.get(url,headers=headers)
    doc = pq(resp.text)
    
    pros = ''
    for pro in doc.items('.baav .pronounce'):
        pros+=pro.text()
    
    
    description = ''
    for li in doc.items('#phrsListTab .trans-container ul li'):
        description +=li.text()
        
    return {'word':word,'音標':pros,'註釋':description}
    

同步代碼

由於requests庫在任什麼時候候只容許有一個訪問結束徹底結束後,才能進行下一次訪問。沒法經過正規途徑拓展成異步,所以這裏使用了monkey補丁

import requests
from pyquery import PyQuery as pq
import gevent
import time
import gevent.monkey gevent.monkey.patch_all()

words = ['good','bad','cool',         'hot','nice','better',         'head','up','down',         'right','left','east']

def synchronous():    start = time.time()    print('同步開始了')    for word in words:        print(fetch_word_info(word))    end = time.time()    print("同步運行時間: %s 秒" % str(end - start))    
#執行同步
synchronous()

有道詞典網站速度比較慢,基本上半秒解決一個詞註釋音標問題。那要是3600詞就須要半個小時,這速度坑啊!

異步代碼

由於requests庫在任什麼時候候只容許有一個訪問結束徹底結束後,才能進行下一次訪問。沒法經過正規途徑拓展成異步,所以這裏使用了monkey補丁

import requests
from pyquery import PyQuery as pq
import gevent
import time
import gevent.monkey gevent.monkey.patch_all()

words = ['good','bad','cool',         'hot','nice','better',         'head','up','down',         'right','left','east']

def asynchronous():    start = time.time()    print('異步開始了')    events = [gevent.spawn(fetch_word_info,word) for word in words]    wordinfos = gevent.joinall(events)    for wordinfo in wordinfos:        #獲取到數據get方法        print(wordinfo.get())    end = time.time()    print("異步運行時間: %s 秒"%str(end-start))

#執行異步
asynchronous()

這速度,酸爽啊

速度與激情

6.44s vs 0.82s,讓咱們從新欣賞一下子這兩個動圖



項目下載地址

連接: https://pan.baidu.com/s/1eT5gJrO 密碼: wad8


數據採集

文本處理分析

圖片數據處理

其餘





本文分享自微信公衆號 - 大鄧和他的Python(DaDengAndHisPython)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索