Python 協程之 Gevent 模塊
Gevent模塊
安裝:pip3 install gevent
Gevent 是一個第三方庫,可以輕松通過gevent實現并發同步或異步編程,在gevent中用到的主要模式是Greenlet, 它是以C擴展模塊形式接入Python的輕量級協程。 Greenlet全部運行在主程序操作系統進程的內部,但它們被協作式地調度。
用法介紹:
g1=gevent.spawn(func,1,,2,3,x=4,y=5)創建一個協程對象g1,spawn括號內第一個參數是函數名,如eat,后面可以有多個參數,可以是位置實參或關鍵字實參, 都是傳給函數eat的 g2=gevent.spawn(func2) g1.join()?#等待g1結束 g2.join()?#等待g2結束 #或者上述兩步合作一步:gevent.joinall([g1,g2]) g1.value#拿到func1的返回值
例:遇到io 主動切換
import?gevent def?eat(name): ????print('%s?eat?1'?%name) ????gevent.sleep(2) ????print('%s?eat?2'?%name) def?play(name): ????print('%s?play?1'?%name) ????gevent.sleep(1) ????print('%s?play?2'?%name) g1=gevent.spawn(eat,'egon') g2=gevent.spawn(play,name='egon') g1.join() g2.join() #或者gevent.joinall([g1,g2]) print('主')
上例gevent.sleep(2)模擬的是gevent可以識別的io阻塞,而time.sleep(2)或其他的阻塞,gevent是不能直接識別的需要用下面一行代碼,打補丁,就可以識別了
from?gevent?import?monkey;monkey.patch_all() import?gevent import?time def?eat(): ????print('eat?food?1') ????time.sleep(2) ????print('eat?food?2') def?play(): ????print('play?1') ????time.sleep(1) ????print('play?2') g1=gevent.spawn(eat) g2=gevent.spawn(play) gevent.joinall([g1,g2]) print('主')
我們可以用threading.current_thread().getName()來查看每個g1和g2,查看的結果為DummyThread-n,即假線程
查看threading.current_thread().getName():
from?gevent?import?monkey;monkey.patch_all() import?threading import?gevent import?time def?eat(): ????print(threading.current_thread().getName()) ????print('eat?food?1') ????time.sleep(2) ????print('eat?food?2') def?play(): ????print(threading.current_thread().getName()) ????print('play?1') ????time.sleep(1) ????print('play?2') g1=gevent.spawn(eat) g2=gevent.spawn(play) gevent.joinall([g1,g2]) print('主')
Gevent之同步與異步
from?gevent?import?spawn,joinall,monkey;monkey.patch_all() import?time def?task(pid): ????""" ????Some?non-deterministic?task ????""" ????time.sleep(0.5) ????print('Task?%s?done'?%?pid) def?synchronous():??#?同步 ????for?i?in?range(10): ????????task(i) def?asynchronous():?#?異步 ????g_l=[spawn(task,i)?for?i?in?range(10)] ????joinall(g_l) ????print('DONE') if?__name__?==?'__main__': ????print('Synchronous:') ????synchronous() ????print('Asynchronous:') ????asynchronous() #??上面程序的重要部分是將task函數封裝到Greenlet內部線程的gevent.spawn。 #??初始化的greenlet列表存放在數組threads中,此數組被傳給gevent.joinall?函數, #??后者阻塞當前流程,并執行所有給定的greenlet任務。執行流程只會在?所有greenlet執行完后才會繼續向下走。
Gevent之應用舉例一
通過gevent實現單線程下的socket并發
注意 :from gevent import monkey;monkey.patch_all()一定要放到導入socket模塊之前,否則gevent無法識別socket的阻塞
server:
from?gevent?import?monkey;monkey.patch_all() from?socket?import?* import?gevent #如果不想用money.patch_all()打補丁,可以用gevent自帶的socket #?from?gevent?import?socket #?s=socket.socket() def?server(server_ip,port): ????s=socket(AF_INET,SOCK_STREAM) ????s.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) ????s.bind((server_ip,port)) ????s.listen(5) ????while?True: ????????conn,addr=s.accept() ????????gevent.spawn(talk,conn,addr) def?talk(conn,addr): ????try: ????????while?True: ????????????res=conn.recv(1024) ????????????print('client?%s:%s?msg:?%s'?%(addr[0],addr[1],res)) ????????????conn.send(res.upper()) ????except?Exception?as?e: ????????print(e) ????finally: ????????conn.close() if?__name__?==?'__main__': ????server('127.0.0.1',8080)
clent:
from?socket?import?* client=socket(AF_INET,SOCK_STREAM) client.connect(('127.0.0.1',8080)) while?True: ????msg=input('>>:?').strip() ????if?not?msg:continue ????client.send(msg.encode('utf-8')) ????msg=client.recv(1024) ????print(msg.decode('utf-8'))
多線程并發多個客戶端:
from?threading?import?Thread from?socket?import?* import?threading def?client(server_ip,port): ????c=socket(AF_INET,SOCK_STREAM)?#套接字對象一定要加到函數內,即局部名稱空間內,放在函數外則被所有線程共享,則大家公用一個套接字對象,那么客戶端端口永遠一樣了 ????c.connect((server_ip,port)) ????count=0 ????while?True: ????????c.send(('%s?say?hello?%s'?%(threading.current_thread().getName(),count)).encode('utf-8')) ????????msg=c.recv(1024) ????????print(msg.decode('utf-8')) ????????count+=1 if?__name__?==?'__main__': ????for?i?in?range(500): ????????t=Thread(target=client,args=('127.0.0.1',8080)) ????????t.start(
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。