1. 程式人生 > >python------線程

python------線程

單純 time() 時間 用戶態 io阻塞 速度 創建線程 let 其他

一、我們知道無論是創建多進程還是創建多線程池來解決問題,都要消耗一定的時間來創建進程、創建線程、以及管理他們之間的切換。

  基於單線程來實現並發,這樣就可以節省創建線程進程所消耗的時間。

二、如何實現在兩個函數之間的切換?

技術分享圖片
def func1():
    print(1)
    yield
    print(3)
    yield

def func2():
    g = func1()
    next(g)
    print(2)
    next(g)
    print(4)
func2()
‘‘‘
1
2
3
4
‘‘‘
切換1 技術分享圖片
def consumer():
    
while True: n = yield print(消費了一個包子%s%n) def producer(): g = consumer() next(g) for i in range(5): print(生產了包子%s%i) g.send(i) producer() ‘‘‘ 生產了包子0 消費了一個包子0 生產了包子1 消費了一個包子1 生產了包子2 消費了一個包子2 生產了包子3 消費了一個包子3 生產了包子4 消費了一個包子4 ‘‘‘
切換2 技術分享圖片
import time
def consumer():
    
‘‘‘任務1:接收數據,處理數據‘‘‘ while True: x=yield def producer(): ‘‘‘任務2:生產數據‘‘‘ g=consumer() next(g) for i in range(10000000): g.send(i) time.sleep(2) start=time.time() producer() #並發執行,但是任務producer遇到io就會阻塞住,並不會切到該線程內的其他任務去執行 stop=time.time() print(stop-start)
yield無法做到遇到io阻塞

  對於單線程下,程序中不可避免的會出現io操作,但如果我們能在自己的程序中(即用戶程序級別,而非操作系統級別)控制單線程下的多個任務能再一個任務遇到io阻塞時就切換到另外一個任務去計算,這樣就保證了該線程能夠最大限度地處於就緒態,即隨時都可以被cpu執行的狀態,相當於我們在用戶程序級別將自己的io操作最大限度地隱藏起來,從而可以迷惑操作系統,讓其看到:該線程好像是一直在計算,io比較少,從而更多的將cpu的執行權限分配給我們的線程。

三、協程

協程:是單線程下的並發,協程是一種用戶態的輕量級線程,即協程是由用戶程序自己控制調度的。

協程的本質:在但線程下,由用戶自己控制一個任務遇到io阻塞了就切換另外一個任務去執行,以此來提升效率。

需要強調的是:

 1:python的線程屬於內核級別的,即由操作系統控制調度(如單線程遇到io或執行時間過長就會被迫交出cpu執行權限,切換其它線程運行)。

 2:單線程內開啟協程,一旦遇到io,就會從應用程序級別(而非操作系統)控制切換,以此來提升效率(非io操作的切換與效率無關)

對比操作系統控制線程的切換,用戶在單線程內控制協程的切換的優缺點:

優點:

 1.協程的切換開銷更小,屬於程序級別的切換,操作系統完全感知不到,因而更加輕量級。

 2.單線程內就可以實現並發的效果,最大限度地利用cpu。

缺點:

 1.協程的本質是單線程下,無法利用多核,可以是一個程序開啟多個進程,每個進程內開啟多個線程,每個線程內開啟協程。

 2.協程指的是單個線程,因而一旦協程出現阻塞,將會阻塞整個線程。

協程的特點:

 1.必須在只有一個單線程裏實現並發

 2.修改共享數據不需加鎖

 3.用戶程序裏自己保存多個控制流的上下文棧

 4.附加:一個協程遇到IO操作自動切換到其它協程(如何實現檢測IO,yield、greenlet都無法實現,就用到了gevent模塊(select機制))

四、greenlet模塊

技術分享圖片
from greenlet import greenlet
def eat(name):
    print(%s eat 1 %name)
    g2.switch(haha)
    print(%s eat 2 %name)
    g2.switch()
def play(name):
    print(%s play 1 %name)
    g1.switch()
    print(%s play 2 %name)
g1=greenlet(eat)
g2=greenlet(play)
g1.switch(hjm)#可以在第一次switch時傳入參數,以後都不需要
‘‘‘
hjm eat 1
haha play 1
hjm eat 2
haha play 2
‘‘‘
greenlet實現狀態切換 技術分享圖片
import time
from greenlet import greenlet   # 在單線程中切換狀態的模塊
def eat1():
    print(吃雞腿1)
    g2.switch()
    time.sleep(5)
    print(吃雞翅2)
    g2.switch()

def eat2():
    print(吃餃子1)
    g1.switch()
    time.sleep(3)
    print(白切雞)

g1 = greenlet(eat1)
g2 = greenlet(eat2)
g1.switch()
‘‘‘
吃雞腿1
吃餃子1
吃雞翅2
白切雞
‘‘‘
greenlet實現狀態切換2

單純的切換(在沒有io的情況下或者沒有重復開辟內存空間的操作),反而會降低程序的執行速度。

技術分享圖片
#順序執行
import time
def f1():
    res=1
    for i in range(100000000):
        res+=i

def f2():
    res=1
    for i in range(100000000):
        res*=i

start=time.time()
f1()
f2()
stop=time.time()
print(run time is %s %(stop-start)) # run time is 10.494175910949707

#切換
from greenlet import greenlet
import time
def f1():
    res=1
    for i in range(100000000):
        res+=i
        g2.switch()

def f2():
    res=1
    for i in range(100000000):
        res*=i
        g1.switch()

start=time.time()
g1=greenlet(f1)
g2=greenlet(f2)
g1.switch()
stop=time.time()
print(run time is %s %(stop-start)) # run time is 63.0725622177124
效率對比

greenlet只是提供了一種比generator更加便捷的切換方式,當切到一個任務執行時如果遇到io,那就原地阻塞,仍然是沒有解決遇到IO自動切換來提升效率的問題。

五、gevent模塊

python------線程