協程是一種用戶態的輕量級線程,又稱微線程。python
協程擁有本身的寄存器上下文和棧,調度切換時,將寄存器上下文和棧保存到其餘地方,在切回來的時候,恢復先前保存的寄存器上下文和棧。所以:協程能保留上一次調用時的狀態(即全部局部狀態的一個特定組合),每次過程重入時,就至關於進入上一次調用的狀態,換種說法:進入上一次離開時所處邏輯流的位置。git
優勢:github
所謂原子操做是指不會被線程調度機制打斷的操做;這種操做一旦開始,就一直運行到結束,中間不會有任何 context switch (切換到另外一個線程)。編程
原子操做能夠是一個步驟,也能夠是多個操做步驟,可是其順序是不能夠被打亂,或者切割掉只執行部分。視做總體是原子性的核心。
缺點:網絡
gevent是python的一個併發框架,以微線程greenlet爲核心,使用了epoll事件監聽機制以及諸多其餘優化而變得高效.多線程
gevent的sleep能夠交出控制權,當咱們在受限於網絡或IO的函數中使用gevent,這些函數會被協做式的調度, gevent的真正能力會獲得發揮。Gevent處理了全部的細節, 來保證你的網絡庫會在可能的時候,隱式交出greenlet上下文的執行權。併發
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') # 建立線程並行執行程序 gevent.joinall([ gevent.spawn(foo), gevent.spawn(bar), ])
執行結果框架
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()
執行輸出dom
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() # 等待執行結束
當一個greenlet遇到IO操做時,好比訪問網絡,就自動切換到其餘的greenlet,等到IO操做完成,再在適當的時候切換回來繼續執行。
因爲IO操做很是耗時,常常使程序處於等待狀態,有了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/'), ])
執行輸出
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個網絡操做是併發執行的,並且結束順序不一樣
參考連接:http://hhkbp2.github.io/gevent-tutorial/