天天看點

多任務之協程

協程

協程的概念

協程,又稱微線程,纖程,也稱為使用者級線程,在不開辟線程的基礎上完成多任務,也就是在單線程的情況下完成多任務,多個任務按照一定順序交替執行 通俗了解隻要在def裡面隻看到一個yield關鍵字表示就是協程

協程也是實作多任務的一種方式

greentlet的介紹

為了更好使用協程來完成多任務,python中的greenlet子產品對其封裝,進而使得切換任務變的更加簡單

使用如下指令安裝greenlet子產品:

pip3 install greenlet
           
import time
import greenlet


# 任務1
def work1():
    for i in range():
        print("work1...")
        time.sleep()
        # 切換到協程2裡面執行對應的任務
        g2.switch()


# 任務2
def work2():
    for i in range():
        print("work2...")
        time.sleep()
        # 切換到第一個協程執行對應的任務
        g1.switch()


if __name__ == '__main__':
    # 建立協程指定對應的任務
    g1 = greenlet.greenlet(work1)
    g2 = greenlet.greenlet(work2)

    # 切換到第一個協程執行對應的任務
    g1.switch()
           

運作效果:

work1...
work2...
work1...
work2...
work1...
work2...
work1...
work2...
work1...
work2...
           

gevent 介紹

greenlet已經實作了協程,但是這個還要人工切換,這裡介紹一個比greenlet更強大而且能夠自動切換任務的第三方庫,那就是gevent。

gevent内部封裝的greenlet,其原理是當一個greenlet遇到IO(指的是input output 輸入輸出,比如網絡、檔案操作等)操作時,比如通路網絡,就自動切換到其他的greenlet,等到IO操作完成,再在适當的時候切換回來繼續執行。

由于IO操作非常耗時,經常使程式處于等待狀态,有了gevent為我們自動切換協程,就保證總有greenlet在運作,而不是等待IO

import gevent

def work(n):
    for i in range(n):
        # 擷取目前協程
        print(gevent.getcurrent(), i)
        #用來模拟一個耗時操作,注意不是time子產品中的sleep
        gevent.sleep()

g1 = gevent.spawn(work, )
g2 = gevent.spawn(work, )
g3 = gevent.spawn(work, )
g1.join()
g2.join()
g3.join()
           

運作結果:

<Greenlet at 0x7fa70ffa1c30: f(5)> 0
<Greenlet at 0x7fa70ffa1870: f(5)> 0
<Greenlet at 0x7fa70ffa1eb0: f(5)> 0
<Greenlet at 0x7fa70ffa1c30: f(5)> 1
<Greenlet at 0x7fa70ffa1870: f(5)> 1
<Greenlet at 0x7fa70ffa1eb0: f(5)> 1
<Greenlet at 0x7fa70ffa1c30: f(5)> 2
<Greenlet at 0x7fa70ffa1870: f(5)> 2
<Greenlet at 0x7fa70ffa1eb0: f(5)> 2
<Greenlet at 0x7fa70ffa1c30: f(5)> 3
<Greenlet at 0x7fa70ffa1870: f(5)> 3
<Greenlet at 0x7fa70ffa1eb0: f(5)> 3
<Greenlet at 0x7fa70ffa1c30: f(5)> 4
<Greenlet at 0x7fa70ffa1870: f(5)> 4
<Greenlet at 0x7fa70ffa1eb0: f(5)> 4
           

注意:

目前程式是一個死循環并且還能有耗時操作,就不需要加上join方法了,因為程式需要一直運作不會退出
import gevent
import time
from gevent import monkey

# 打更新檔,讓gevent架構識别耗時操作,比如:time.sleep,網絡請求延時
monkey.patch_all()


# 任務1
def work1(num):
    for i in range(num):
        print("work1....")
        time.sleep()
        # gevent.sleep(0.2)

# 任務1
def work2(num):
    for i in range(num):
        print("work2....")
        time.sleep()
        # gevent.sleep(0.2)



if __name__ == '__main__':
    # 建立協程指定對應的任務
    g1 = gevent.spawn(work1, )
    g2 = gevent.spawn(work2, )

    while True:
        print("主線程中執行")
        time.sleep()
           

執行結果:

主線程中執行
work1....
work2....
work1....
work2....
work1....
work2....
主線程中執行
主線程中執行
主線程中執行
..省略..
           

程序 線程 協程對比

程序 線程 協程關系

  • 一個程序至少有一個線程,程序裡面可以有多個線程
  • 一個線程裡面可以有多個協程

程序 線程 協程對比

  • 程序是資源配置設定的機關
  • 線程是作業系統排程的機關
  • 程序切換需要的資源最大,效率很低
  • 線程切換需要的資源一般,效率一般(當然了在不考慮GIL的情況下)
  • 協程切換任務資源很小,效率高
  • 多程序、多線程根據cpu核數不一樣可能是并行的,但是協程是在一個線程中 是以是并發

總結:

程序、線程、協程都是可以完成多任務的,可以根據自己實際開發的需要選擇使用

由于線程、協程需要的資源很少,是以使用線程和協程的幾率最大

開辟協程需要的資源最少