python使用協(xié)程實(shí)現(xiàn)并發(fā)操作的方法詳解
本文實(shí)例講述了python使用協(xié)程實(shí)現(xiàn)并發(fā)操作的方法。分享給大家供大家參考,具體如下:
協(xié)程
協(xié)程是一種用戶態(tài)的輕量級(jí)線程,又稱微線程。
協(xié)程擁有自己的寄存器上下文和棧,調(diào)度切換時(shí),將寄存器上下文和棧保存到其他地方,在切回來(lái)的時(shí)候,恢復(fù)先前保存的寄存器上下文和棧。因此:協(xié)程能保留上一次調(diào)用時(shí)的狀態(tài)(即所有局部狀態(tài)的一個(gè)特定組合),每次過(guò)程重入時(shí),就相當(dāng)于進(jìn)入上一次調(diào)用的狀態(tài),換種說(shuō)法:進(jìn)入上一次離開時(shí)所處邏輯流的位置。
優(yōu)點(diǎn):
- 無(wú)需線程上下文切換的開銷
- 無(wú)需原子操作鎖定及同步的開銷
- 方便切換控制流,簡(jiǎn)化編程模型
- 高并發(fā)+高擴(kuò)展性+低成本:一個(gè)CPU支持上萬(wàn)的協(xié)程都不是問(wèn)題。所以很適合用于高并發(fā)處理。
所謂原子操作是指不會(huì)被線程調(diào)度機(jī)制打斷的操作;這種操作一旦開始,就一直運(yùn)行到結(jié)束,中間不會(huì)有任何 context switch (切換到另一個(gè)線程)。
原子操作可以是一個(gè)步驟,也可以是多個(gè)操作步驟,但是其順序是不可以被打亂,或者切割掉只執(zhí)行部分。視作整體是原子性的核心。
缺點(diǎn):
- 無(wú)法利用多核資源:協(xié)程的本質(zhì)是個(gè)單線程,它不能同時(shí)將 單個(gè)CPU 的多個(gè)核用上,協(xié)程需要和進(jìn)程配合才能運(yùn)行在多CPU上.當(dāng)然我們?nèi)粘K帉懙慕^大部分應(yīng)用都沒有這個(gè)必要,除非是cpu密集型應(yīng)用。
- 進(jìn)行阻塞(Blocking)操作(如IO時(shí))會(huì)阻塞掉整個(gè)程序
使用Gevent
gevent是python的一個(gè)并發(fā)框架,以微線程greenlet為核心,使用了epoll事件監(jiān)聽機(jī)制以及諸多其他優(yōu)化而變得高效.
- 簡(jiǎn)單示例
gevent的sleep可以交出控制權(quán),當(dāng)我們?cè)谑芟抻诰W(wǎng)絡(luò)或IO的函數(shù)中使用gevent,這些函數(shù)會(huì)被協(xié)作式的調(diào)度, gevent的真正能力會(huì)得到發(fā)揮。Gevent處理了所有的細(xì)節(jié), 來(lái)保證你的網(wǎng)絡(luò)庫(kù)會(huì)在可能的時(shí)候,隱式交出greenlet上下文的執(zhí)行權(quán)。
import gevent def foo(): print('running in foo') gevent.sleep(0) print('com back from bar in to foo') def bar(): print('running in bar') gevent.sleep(0) print('com back from foo in to bar') # 創(chuàng)建線程并行執(zhí)行程序 gevent.joinall([ gevent.spawn(foo), gevent.spawn(bar), ])
執(zhí)行結(jié)果
running in foo
running in bar
com back from bar in to foo
com back from foo in to bar
- 同步異步
import random import gevent def task(pid): gevent.sleep(random.randint(0, 2) * 0.001) print('Task %s done' % pid) def synchronous(): for i in range(1, 10): task(i) def asynchronous(): threads = [gevent.spawn(task, i) for i in range(10)] gevent.joinall(threads) print('Synchronous:') synchronous() print('Asynchronous:') asynchronous()
執(zhí)行輸出
Synchronous:
Task 1 done
Task 2 done
Task 3 done
Task 4 done
Task 5 done
Task 6 done
Task 7 done
Task 8 done
Task 9 done
Asynchronous:
Task 1 done
Task 4 done
Task 5 done
Task 9 done
Task 6 done
Task 0 done
Task 2 done
Task 3 done
Task 7 done
Task 8 done
- 以子類的方法使用協(xié)程
可以子類化Greenlet類,重載它的_run方法,類似多線程和多進(jìn)程模塊
import gevent from gevent import Greenlet class Test(Greenlet): def __init__(self, message, n): Greenlet.__init__(self) self.message = message self.n = n def _run(self): print(self.message, 'start') gevent.sleep(self.n) print(self.message, 'end') tests = [ Test("hello", 3), Test("world", 2), ] for test in tests: test.start() # 啟動(dòng) for test in tests: test.join() # 等待執(zhí)行結(jié)束
- 使用monkey patch修改系統(tǒng)標(biāo)準(zhǔn)庫(kù)(自動(dòng)切換協(xié)程)
當(dāng)一個(gè)greenlet遇到IO操作時(shí),比如訪問(wèn)網(wǎng)絡(luò),就自動(dòng)切換到其他的greenlet,等到IO操作完成,再在適當(dāng)?shù)臅r(shí)候切換回來(lái)繼續(xù)執(zhí)行。
由于IO操作非常耗時(shí),經(jīng)常使程序處于等待狀態(tài),有了gevent為我們自動(dòng)切換協(xié)程,就保證總有g(shù)reenlet在運(yùn)行,而不是等待IO。
由于切換是在IO操作時(shí)自動(dòng)完成,所以gevent需要修改Python自帶的一些標(biāo)準(zhǔn)庫(kù),這一過(guò)程在啟動(dòng)時(shí)通過(guò)monkey patch完成
import gevent import requests from gevent import monkey monkey.patch_socket() def task(url): r = requests.get(url) print('%s bytes received from %s' % (len(r.text), url)) gevent.joinall([ gevent.spawn(task, 'https://www.baidu.com/'), gevent.spawn(task, 'https://www.qq.com/'), gevent.spawn(task, 'https://www.jd.com/'), ])
執(zhí)行輸出
2443 bytes received from https://www.baidu.com/
108315 bytes received from https://www.jd.com/
231873 bytes received from https://www.qq.com/
可以看出3個(gè)網(wǎng)絡(luò)操作是并發(fā)執(zhí)行的,而且結(jié)束順序不同
參考鏈接:http://hhkbp2.github.io/gevent-tutorial/
更多關(guān)于Python相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《Python進(jìn)程與線程操作技巧總結(jié)》、《Python數(shù)據(jù)結(jié)構(gòu)與算法教程》、《Python函數(shù)使用技巧總結(jié)》、《Python字符串操作技巧匯總》、《Python入門與進(jìn)階經(jīng)典教程》、《Python+MySQL數(shù)據(jù)庫(kù)程序設(shè)計(jì)入門教程》及《Python常見數(shù)據(jù)庫(kù)操作技巧匯總》
希望本文所述對(duì)大家Python程序設(shè)計(jì)有所幫助。
- Python+redis通過(guò)限流保護(hù)高并發(fā)系統(tǒng)
- Python統(tǒng)計(jì)時(shí)間內(nèi)的并發(fā)數(shù)代碼實(shí)例
- python如何使用socketserver模塊實(shí)現(xiàn)并發(fā)聊天
- selenium+python實(shí)現(xiàn)登陸QQ郵箱并發(fā)送郵件功能
- 使用Python paramiko模塊利用多線程實(shí)現(xiàn)ssh并發(fā)執(zhí)行操作
- Python3并發(fā)寫文件與Python對(duì)比
- Python aiohttp百萬(wàn)并發(fā)極限測(cè)試實(shí)例分析
- Python高并發(fā)解決方案實(shí)現(xiàn)過(guò)程詳解
相關(guān)文章
虛擬環(huán)境及venv和virtualenv的區(qū)別說(shuō)明
這篇文章主要介紹了虛擬環(huán)境及venv和virtualenv的區(qū)別說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-02-02使用python將請(qǐng)求的requests headers參數(shù)格式化方法
今天小編就為大家分享一篇使用python將請(qǐng)求的requests headers參數(shù)格式化方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-01-01解決Python3 struct報(bào)錯(cuò)argument for 's'&
這篇文章主要為大家介紹了解決Python3 struct報(bào)錯(cuò)argument for 's' must be a bytes object方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08多個(gè)python文件調(diào)用logging模塊報(bào)錯(cuò)誤
這篇文章主要介紹了多個(gè)python文件調(diào)用logging模塊產(chǎn)生錯(cuò)誤,需要的朋友可以參考下2020-02-02python+os根據(jù)文件名自動(dòng)生成文本
這篇文章主要為大家詳細(xì)介紹了python+os根據(jù)文件名自動(dòng)生成文本,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-03-03Python爬蟲抓取論壇關(guān)鍵字過(guò)程解析
這篇文章主要介紹了Python爬蟲抓取論壇關(guān)鍵字過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-10-10