python 基礎(五)協程 —— 微線程 greenlet gevent

python基礎系列 正在持續更新中:)

回憶進程與線程 引入

我們之前說,有比較耗時的操作得交給線程來幹,因爲進程是同步調用,阻塞執行,串行執行的,那麼我們就要用線程,異步調用,非阻塞並行執行,這樣的話才能提升效率。
問題來了,假設線程中也有幾個子任務,耗時,然後我們程序都是串行的,也就一個Thread裏面的run()都是串行執行的,一個子任務,比方下載很慢,其他任務都阻塞在那,就效率又很低了。
這時的線程就好像以前的進程一樣尷尬。
我們python就出來一個微線程,可以並行操作的,線程的子任務部分,也稱爲
攜程
協程(Coroutine) cor - routine 也可以看出並行的含義

比如,我們有很多任務要做,打遊戲,學習python,配朋友,這就是三個進程。在打遊戲中,我有 黑暗之魂1,2,3,上古卷軸5,也就是4個線程,當然,上古卷軸5有100個mod,我需要更新100個mod,就有了100個協程。因爲更新比較耗時,必須並行調用 更新mod的任務。

greenlet

python封裝了庫greenlet來實現協程,我們來看個例子:

#-*- utf-8 -*-
from time import sleep,time
from greenlet import greenlet
def taskA():
    for i in range(5):
        print('A'+str(i))
        gB.switch()
        sleep(1)  # 模擬網絡阻塞 耗時操作


def taskB():
    for i in range(5):
        print('B' + str(i))
        gC.switch()
        sleep(1)  # 模擬網絡阻塞 耗時操作



def taskC():
    for i in range(5):
        print('C' + str(i))
        gA.switch()
        sleep(1)  # 模擬網絡阻塞 耗時操作


if __name__ == "__main__":
    t1 = time()
    gA = greenlet(run = taskA)
    gB = greenlet(run = taskB)
    gC = greenlet(run = taskC)

    gA.switch()
    
    t2 = time()
    print("time consume",t2-t1)

類似process thread, greenlet也必須傳target參數(他命名爲run參數 反正一個意思)
這裏主要是實現A B C並行輪詢,但是我們可以看到這樣寫很蠢,需要人工切換,而且效率也不算高,於是官方的greenlet不好用,就有大佬弄出來gevent庫(第三方),在greenlet基礎上再封裝一波,可以實現自動切換。

gevent

利用spawn函數傳要做的事(taskA B C),spawn有 產卵; 引發; 引起 這幾個意思,有點run的含義。
注意,我們之前在進程裏面遇到過,主進程啓動完子進程start以後就跑路了,導致子進程也gg,這樣我們就用join阻塞,讓主進程別溜,這裏也是同樣的道理。

#-*- utf-8 -*-
from time import sleep,time
from gevent import monkey,spawn

def taskA():
    for i in range(3):
        print('A'+str(i))
        sleep(1)  # 模擬網絡阻塞 耗時操作


def taskB():
    for i in range(3):
        print('B' + str(i))
        sleep(1)  # 模擬網絡阻塞 耗時操作



def taskC():
    for i in range(3):
        print('C' + str(i))
        sleep(1)  # 模擬網絡阻塞 耗時操作


if __name__ == "__main__":
    t1 = time()
    gA = spawn(run = taskA)
    gB = spawn(run = taskB)
    gC = spawn(run = taskC)

    gA.join()
    gB.join()
    gC.join()
    t2 = time()
    print("time consume",t2-t1)

結果:

A0
A1
A2
B0
B1
B2
C0
C1
C2
time consume 9.041131258010864

我們發現,
1: 並沒有輪詢ABC ABC 這樣的啊
2: 並沒有並行啊2333 其實上面的greenlet也沒有實現並行??? 還是9s

monkey

我們的time不適合這個場合的,於是利用monkey.patch_all()

#-*- utf-8 -*-
import time
from gevent import monkey,spawn
monkey.patch_all()

def taskA():
    for i in range(3):
        print('A'+str(i))
        time.sleep(1)  # 模擬網絡阻塞 耗時操作


def taskB():
    for i in range(3):
        print('B' + str(i))
        time.sleep(1)  # 模擬網絡阻塞 耗時操作



def taskC():
    for i in range(3):
        print('C' + str(i))
        time.sleep(1)  # 模擬網絡阻塞 耗時操作


if __name__ == "__main__":
    t1 = time.time()
    gA = spawn(run = taskA)
    gB = spawn(run = taskB)
    gC = spawn(run = taskC)

    gA.join()
    gB.join()
    gC.join()
    t2 = time.time()
    print("time consume",t2-t1)

這裏 必須time.sleep才能生效 因爲monkey在背後偷偷重載替換了原生的sleep,你直接from time import sleep, monkey匹配不到
結果:

A0
B0
C0
A1
B1
C1
A2
B2
C2
time consume 3.036893606185913

Process finished with exit code 0

可見,比較完美實現了並行,time consume 時間消耗 從9s 並行變成了 3s

總結

我們直接用gevent monkey就行了 其他的執行方式太麻煩了,簡直就是再造輪子:)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章