Python 之协程
生活随笔
收集整理的這篇文章主要介紹了
Python 之协程
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
?
多線程并發、包括線程池,是操作系統控制的并發。如果是單線程,可以通過協程實現單線程下的并發。
協程 又稱微線程,是一種用戶態的輕量級線程,由用戶程序自己控制調度。
python的線程屬于內核級別的,由操作系統控制調度(如單線程遇到io或執行時間過長就會被迫交出cpu執行權限,切換其他線程運行)
而單線程里開啟協程,一旦遇到io,由用戶自己控制調度。
特點:
1、單線程里并發
2、修改共享數據不需枷鎖
3、用戶程序里保存多個控制流的上下文棧
4、附加:一個協程遇到IO操作自動切換到其他協程(yield,greenlet都無法實現檢測IO,用gevent模塊的select機制可以)
并發:
# 注意到consumer函數是一個generator(生成器): # 任何包含yield關鍵字的函數都會自動成為生成器(generator)對象 def consumer():r = ''while True:# 3、consumer通過yield拿到消息,處理,又通過yield把結果傳回;# yield指令具有return關鍵字的作用。然后函數的堆棧會自動凍結(freeze)在這一行。# 當函數調用者的下一次利用next()或generator.send()或for-in來再次調用該函數時,# 就會從yield代碼的下一行開始,繼續執行,再返回下一次迭代結果。通過這種方式,迭代器可以實現無限序列和惰性求值。n = yield rif not n:returnprint('[CONSUMER] ←← Consuming %s...' % n)time.sleep(1)r = '200 OK'def produce():# 1、首先調用c.next()啟動生成器c = consumer()next(c)n = 0while n < 5:n = n + 1print('[PRODUCER] →→ Producing %s...' % n)# 2、然后,一旦生產了東西,通過c.send(n)切換到consumer執行;cr = c.send(n)# 4、produce拿到consumer處理的結果,繼續生產下一條消息;print('[PRODUCER] Consumer return: %s' % cr)# 5、produce決定不生產了,通過c.close()關閉consumer,整個過程結束。 c.close()if __name__ == '__main__':start_time = time.time()# 6、整個流程無鎖,由一個線程執行,produce和consumer協作完成任務,所以稱為“協程”,在一個線程里協作完成。res = produce()# consumer(res)end_time = time.time()print(end_time-start_time)?
串行:
def produce():n = 0res = []while n < 5:n = n + 1print('[PRODUCER] →→ Producing %s...' % n)res.append(n)return resdef consumer(res):passif __name__ == '__main__':start_time = time.time()# 6、整個流程無鎖,由一個線程執行,produce和consumer協作完成任務,所以稱為“協程”,在一個線程里協作完成。res = produce()consumer(res)end_time = time.time()print(end_time-start_time)?
greenlet模塊,方便切換,但遇到IO時,不會自動切換到其他任務
from greenlet import greenletdef eat(name):print('%s is eating 1'%name)gr2.switch('zhanwu')print('%s is eating 2' % name)gr2.switch()def play(name):print('%s is playing 1' % name)gr1.switch()print('%s is playing 2' % name)gr1 = greenlet(eat) gr2 = greenlet(play) gr1.switch('egon') #切換的時候傳參?
gevent模塊,異步提交任務,遇到IO時可實現自動切換。
異步提交任務后,主線程結束的話,會導致子線程任務完不成,通過sleep或join實現主線程不死直到子線程運行結束。
from gevent import monkey monkey.patch_all() #將下面的阻塞操作變為非阻塞操作,這是用gevent必須的 def eat(name):print('%s is eating 1'%name)gevent.sleep(3)print('%s is eating 2' % name)def play(name):print('%s is playing 1' % name)gevent.sleep(4)print('%s is playing 2' % name)gevent.joinall([gevent.spawn(eat,'egon'),gevent.spawn(play,'egon')] )?
基于gevent實現單線程下的并發。
如果開多個進程,每個進程里開多個線程,每個線程里再開協程,會大大提升效率。server端 from gevent import monkey,spawn;monkey.patch_all() from socket import *def talk(conn):while True:try:data = conn.recv(1024)if not data: breakconn.send(data.upper())except ConnectionResetError:breakconn.close()def server(ip,port): # 來一個客戶端,起一個connserver = socket(AF_INET, SOCK_STREAM)server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)server.bind((ip, port))server.listen(5)while True:conn,addr = server.accept()spawn(talk,conn)server.close()if __name__ == '__main__':g = spawn(server,'127.0.0.1',8087)g.join()client端: from socket import * from threading import Thread,currentThread def client():client = socket(AF_INET, SOCK_STREAM)client.connect(('127.0.0.1',8087))while True:client.send(('%s say hello'%currentThread().getName()).encode('utf8'))data = client.recv(1024)print(data.decode('utf-8'))if __name__ == '__main__':for i in range(500):t = Thread(target=client)t.start()?
轉載于:https://www.cnblogs.com/stin/p/8549051.html
總結
以上是生活随笔為你收集整理的Python 之协程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [译]php和curl_multi_ex
- 下一篇: 2017-2018-2点集拓扑