python編程使用協程并發(fā)的優(yōu)缺點
協程
協程是一種用戶態(tài)的輕量級線程,又稱微線程。
協程擁有自己的寄存器上下文和棧,調度切換時,將寄存器上下文和棧保存到其他地方,在切回來的時候,恢復先前保存的寄存器上下文和棧。因此:協程能保留上一次調用時的狀態(tài)(即所有局部狀態(tài)的一個特定組合),每次過程重入時,就相當于進入上一次調用的狀態(tài),換種說法:進入上一次離開時所處邏輯流的位置。
優(yōu)點:
1.無需線程上下文切換的開銷
2.無需原子操作鎖定及同步的開銷
3.方便切換控制流,簡化編程模型
4.高并發(fā)+高擴展性+低成本:一個CPU支持上萬的協程都不是問題。所以很適合用于高并發(fā)處理。
所謂原子操作是指不會被線程調度機制打斷的操作;這種操作一旦開始,就一直運行到結束,中間不會有任何 context switch (切換到另一個線程)。
原子操作可以是一個步驟,也可以是多個操作步驟,但是其順序是不可以被打亂,或者切割掉只執(zhí)行部分。視作整體是原子性的核心。
缺點:
1.無法利用多核資源:協程的本質是個單線程,它不能同時將 單個CPU 的多個核用上,協程需要和進程配合才能運行在多CPU上.當然我們日常所編寫的絕大部分應用都沒有這個必要,除非是cpu密集型應用。
2.進行阻塞(Blocking)操作(如IO時)會阻塞掉整個程序
使用Gevent
gevent是python的一個并發(fā)框架,以微線程greenlet為核心,使用了epoll事件監(jiān)聽機制以及諸多其他優(yōu)化而變得高效.
•簡單示例
gevent的sleep可以交出控制權,當我們在受限于網絡或IO的函數中使用gevent,這些函數會被協作式的調度, gevent的真正能力會得到發(fā)揮。Gevent處理了所有的細節(jié), 來保證你的網絡庫會在可能的時候,隱式交出greenlet上下文的執(zhí)行權。
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í)行結果
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
•以子類的方法使用協程
可以子類化Greenlet類,重載它的_run方法,類似多線線程和多進程模塊
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() # 啟動 for test in tests: test.join() # 等待執(zhí)行結束
•使用monkey patch修改系統(tǒng)標準庫(自動切換協程)
當一個greenlet遇到IO操作時,比如訪問網絡,就自動切換到其他的greenlet,等到IO操作完成,再在適當的時候切換回來繼續(xù)執(zhí)行。
由于IO操作非常耗時,經常使程序處于等待狀態(tài),有了gevent為我們自動切換協程,就保證總有greenlet在運行,而不是等待IO。
由于切換是在IO操作時自動完成,所以gevent需要修改Python自帶的一些標準庫,這一過程在啟動時通過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個網絡操作是并發(fā)執(zhí)行的,而且結束順序不同
參考鏈接:http://hhkbp2.github.io/gevent-tutorial/
總結
以上所述是小編給大家介紹的python編程使用協程并發(fā)的優(yōu)缺點,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對腳本之家網站的支持!
相關文章
以一個投票程序的實例來講解Python的Django框架使用
這篇文章主要介紹了以一個投票程序的實例來講解Python的Django框架使用,Django是Python世界中人氣最高的MVC框架,需要的朋友可以參考下2016-02-02