目錄
- 一、什么是協(xié)程
- 二、為什么要有協(xié)程
- 三、協(xié)程的優(yōu)缺點(diǎn)
- 四、如何實(shí)現(xiàn)協(xié)程
-
五、Gevent模塊
- 5.1 模塊的安裝
- 5.2 用法介紹
- 5.3 代碼實(shí)例
- 六、gevent之應(yīng)用
一、什么是協(xié)程
協(xié)程: 就是 單線程 下 實(shí)現(xiàn)并發(fā)
協(xié)程概念本質(zhì)是程序員抽象出來(lái)的,是人為的控制通過(guò)程序的IO去進(jìn)行切換任務(wù)的執(zhí)行
并發(fā):任務(wù)切換+保存狀態(tài)
二、為什么要有協(xié)程
自己控制切換要比操作系統(tǒng)切換快的多.降低了單個(gè)線程的io堵塞時(shí)間,也就是實(shí)現(xiàn)了單線程下效率最高.
三、協(xié)程的優(yōu)缺點(diǎn)
-
優(yōu)點(diǎn):
自己控制切換要比操作系統(tǒng)切換快的多
-
缺點(diǎn):
- 需要自己要檢測(cè)所有的io,但凡有一個(gè)阻塞整體都跟著阻塞.
- 無(wú)法利用多核優(yōu)勢(shì).
四、如何實(shí)現(xiàn)協(xié)程
其實(shí)協(xié)程的本質(zhì)就是在單線程下實(shí)現(xiàn)并發(fā),也就是通過(guò)生成器
yield
和
next
進(jìn)行迭代生成器,實(shí)現(xiàn)切換任務(wù)和保存任務(wù)。
在Python中我們需要使用gevnet模塊來(lái)實(shí)現(xiàn)協(xié)程
五、Gevent模塊
Gevent 是一個(gè)第三方庫(kù),可以輕松通過(guò)gevent實(shí)現(xiàn)并發(fā)同步或異步編程,在gevent中用到的主要模式是Greenlet,它是以C擴(kuò)展模塊形式接入Python的輕量級(jí)協(xié)程。 Greenlet全部運(yùn)行在主程序操作系統(tǒng)進(jìn)程的內(nèi)部,但它們被協(xié)作式地調(diào)度。
5.1 模塊的安裝
安裝:
pip3 install gevent
5.2 用法介紹
g1=gevent.spawn(func,1,,2,3,x=4,y=5)
:創(chuàng)建一個(gè)協(xié)程對(duì)象g1,spawn括號(hào)內(nèi)第一個(gè)參數(shù)是函數(shù)名,如eat,后面可以有多個(gè)參數(shù),可以是位置實(shí)參或關(guān)鍵字實(shí)參,都是傳給函數(shù)eat的
g2=gevent.spawn(func2)
g1.join()
:等待g1結(jié)束
g2.join()
:等待g2結(jié)束
上述兩步合作一步:
gevent.joinall([g1,g2])
g1.value
:拿到func1的返回值
5.3 代碼實(shí)例
通過(guò) from gevent import monkey;
monkey.patch_all() 去補(bǔ)丁,捕獲所以IO
from gevent import monkey;monkey.patch_all()
必須放到被打補(bǔ)丁者的前面,如time,socket模塊之前。
import time
from gevent import monkey;monkey.patch_all()
'''打了一個(gè)補(bǔ)丁,它可以實(shí)現(xiàn)捕獲非gevent的所有io'''
import gevent
def eat():
print('eat 1')
time.sleep(2)
# gevent.sleep(2) # 可以這樣單獨(dú)捕捉阻塞,但是太麻煩,所以直接打補(bǔ)丁,捕捉運(yùn)行期間的全部IO
print('eat 2')
def play():
print('play 1')
# 瘋狂的計(jì)算呢沒(méi)有io
time.sleep(3)
# gevent.sleep(3) # 可以這樣單獨(dú)捕捉阻塞,但是太麻煩,所以直接打補(bǔ)丁,捕捉運(yùn)行期間的全部IO
print('play 2')
'''
gevent實(shí)現(xiàn)協(xié)程的模塊,它可以捕獲單線程中的io并去切換任務(wù)
'''
if __name__ == '__main__':
# 把本該串行的代碼通過(guò)協(xié)程完成單線程并行
start = time.time()
g1 = gevent.spawn(eat) # 創(chuàng)建一個(gè)協(xié)程對(duì)象
g2 = gevent.spawn(play)
# g1.join() # 等待回收協(xié)程對(duì)象
# g2.join()
gevent.joinall([g1,g2]) # 把上面兩步并一步
end = time.time()
print(end-start)
六、gevent之應(yīng)用
通過(guò)gevent實(shí)現(xiàn)單線程下的socket并發(fā)
注意: from gevent import monkey;monkey.patch_all()一定要放到導(dǎo)入socket模塊之前,否則gevent無(wú)法識(shí)別socket的阻塞。
服務(wù)器
import socket
from gevent import monkey; monkey.patch_all() # 打補(bǔ)丁
import gevent
'''
基于協(xié)程的socket通訊
協(xié)程:?jiǎn)尉€程下實(shí)現(xiàn)并發(fā)
'''
def connec_interface(conn,addr):
while 1:
try:
data = conn.recv(1024)
if not data:
break
print(f"來(lái)自{addr}的消息:",data.decode("utf8"))
data = input(f"與{addr}聊天")
conn.send(data.encode("utf8"))
except:
break
if __name__ == '__main__':
server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(("127.0.0.1",8080))
server.listen(5)
while 1:
print("等待連接。。。")
conn,addr = server.accept()
print("連接成功")
gevent.spawn(connec_interface, conn, addr) # 創(chuàng)建一個(gè)協(xié)程對(duì)象
客戶(hù)端
import socket
client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
client.connect(("127.0.0.1",8080))
while 1:
msg = input("請(qǐng)輸入內(nèi)容")
client.send(msg.encode("utf8"))
data = client.recv(1024)
if not data:
break
print(data.decode("utf8"))
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫(xiě)作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
