協程:基於單線程來實現併發 ,又稱微程,纖程(Coroutine) python
併發的本質 :切換 + 保存狀態 即由用戶程序本身控制調度的編程
(1 協程的本質就是在單線程下由用戶控制一個任務遇到阻塞就切換到另一個任務執行以此來提高效率多線程
2 python線程屬於內核級別的,即由操做系統控制(遇io或時間過長被迫交出cpu執行權限)併發
3 單線程開啓協程,一旦遇io就會從應用程序級別(而非操做系統)控制切換,以此來提高效率異步
協程的優勢:socket
1 協程的切換開銷小,屬於程序級別的切換操做系統感知不到異步編程
2 單線程內就能夠實現併發的效果,最大限度地利用cpu函數
缺點:spa
1 協程的本質是單線程下,沒法利用多核,程序 ——》進程———》》線程————》》協程操作系統
2協程是單個線程,於是一旦協和出現阻塞,將會阻塞整個線程
協程的特色:
1,單線程併發 2修改共享數據不須要加鎖 3 在本身的的程序裏保存多個控制上下文棧
上下文棧 :(每一次代碼執行和函數調用都會產生一個執行環境,稱爲執行上下文。
一個執行上下文(caller)又能夠激活(調用)另外一個執行上下文(callee),這時caller會暫停自身的執行把控制權交給callee進入callee的執行上下文,callee執行完畢後將控制權交回caller,callee能夠用return或者拋出Exception來結束本身的執行。)
多個執行上下文會造成執行上下文棧,最頂層是當前執行上下文,底層是全局執行上下文。
切換的狀態:1 發生阻塞(IO) 2計算時間過長
對於第二種純計算的切換則會下降效率:
第一種任務在遇到io切,在利用阻塞的時間完成任務二的計算,效率的提高就在於此
yield 的狀態保存屬於代碼級別的
yield生成器須要初始化一次生成器而後再調用 send /
greenlet模塊能夠很是簡單的實現多個任務直接的切換 ,但當切換任務遇到 io 則原地阻塞仍沒有解決
遇到io 自動切換來提高效率的問題
Gevent模塊:對於既有計算操做又有阻塞操做能夠在執行任務1遇到阻塞就利用阻塞的時間去執行任務2來提高效率 能夠實現併發同步或異步編程
用法:
g1=gevent.spawn() #建立一個協程對象g1 ,spawn() 第一個參數是函數名
g2 = gevent.spawn()
g1.join() #等待g1結果
g2.join() #等待g1結果
#或者上述二者合做: gevent.joinall([g1,g2])
g1.value #拿到func1 的返回值
send 能夠把一個函數的結果傳給另一個函數,以此來實現單線程之間的切換
對於單線程下,咱們不可避免的出現io操做,但若是咱們能在本身的程序中控制單線程下的多個任務
能在一個任務遇到io阻塞時就切換到另一個任務去計算,這樣就保證了該線程可以在最大限度下處於
就緒狀態(隨時均可以被cpu執行的狀態)
多線程併發套接字:
服務端:
from gevent import monkey;monkey.patch_all()
import gevent
from multiprocessing import Process
from socket import *
def talk(conn,addr):
while True:
try:
data=conn.recv(1024)
print('%s:%s [%s]' %(addr[0],addr[1],data))
if not data:break
conn.send(data.upper())
except ConnectionResetError:
break
conn.close()
if __name__ == '__main__':
# server('127.0.0.1',8091)
s = socket(AF_INET, SOCK_STREAM)
s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
s.bind(('127.0.0.1',8090))
s.listen(5)
while True:
conn, addr = s.accept()
print('%s:%s' % (addr[0], addr[1]))
g1 = gevent.spawn(talk, conn, addr)
客戶端:
from socket import *c=socket(AF_INET,SOCK_STREAM)c.connect(('127.0.0.1',8090))while True: msg=input('>>: ').strip() if not msg:continue c.send(msg.encode('utf-8')) data=c.recv(1024) print(data.decode('utf-8'))