協程是一種用戶態的輕量級線程
協程擁有自己的寄存器上下文和棧,能保留上一次調用時的狀態。每次過程衝入時,進入上一次離開時所處邏輯流的位置。
協程是串行工作,同一時間只能執行一個攜程,不需要加鎖。但是進行阻塞(Blocking)操作(如IO操作時)會阻塞掉整個程序。
通過協程在單線程裏實現併發效果。
適合做高併發處理。
yield關鍵字
send()
解決協程阻塞:
greenlet 模塊
gevent 模塊(基於greenlet模塊)
gevent.spawn()啓動一個協程
範例:單線程下併發執行一個函數
from urllib.request import urlopen
import gevent
def f(url):
print('GET: %s' %url)
resp = urlopen(url)
data = resp.read()
print('%d byte received form %s.'%(len(data),url))
#單線程下併發的執行一個下載任務
gevent.joinall([ #開啓了三個協程
gevent.spawn(f,'https://www.python.org/'),
gevent.spawn(f,'https://www.yahoo.com'),
gevent.spawn(f,'https://github.com/'),
])
結果:
GET: https://www.python.org/
47410 byte received form https://www.python.org/.
GET: https://www.yahoo.com
465372 byte received form https://www.yahoo.com.
GET: https://github.com/
92691 byte received form https://github.com/.
範例:單線程下的多socket併發
server side
import gevent
from gevent import socket,monkey
monkey.patch_all() #monkey 將python中的很多變成非阻塞的形式
def server(port):
s = socket.socket()
s.bind(('0.0.0.0',port))
s.listen(500)
while True:
cli,addr = s.accept()
gevent.spawn(handle_request,cli) #啓動了一個協程,將cli客戶端穿了進來
def handle_request(s):
try:
while True:
data = s.recv(1024)
print('recv:',data)
s.send(data)
if not data: #沒有數據就斷開了
s.shutdown(socket.SHUT_WR) #把客戶端銷燬
except Exception as ex:
print(ex)
finally:
s.close() #把跟這個客戶端的鏈接關掉
if __name__ =='__main__':
server(8001) #啓動在8001端口
client side
import socket
HOST = 'localhost'
PORT = 8001
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((HOST,PORT))
while True:
msg = bytes(input('>>:'),encoding='utf8')
s.sendall(msg)
data = s.recv(1024)
print('Reveived',repr(data))
s.close()