單線程如何實現併發:python
# cpu正在運行一個任務,會在兩種狀況下切走去執行其餘的任務(切換由操做系統強制控制), - 一種狀況是該任務發生了阻塞 - 另外一種狀況是該任務計算的時間過長或有一個優先級更高的程序替代了它 # 第二種狀況切換並不能提升效率,反而會下降效率。 # 基於以前的基礎知識,可使用yield,函數生成器來實現 單線程內程序間的切換和保存狀態
單線程下,咱們不可避免程序中出現IO操做,但若是咱們能在本身的程序中(即用戶程序級別,而非操做系統級別)控制單線程下的多個任務,在一個任務遇到IO阻塞時就切換到另一個任務去計算,這樣就保證了該線程可以最大限度地處於就緒態,即隨時均可以被CPU執行的狀態,至關於咱們在用戶程序級別將本身的IO操做最大限度地隱藏起來,從而能夠迷惑操做系統,讓其看到:該線程好像是一直在計算,IO比較少,從而更多的將CPU的執行權限分配給咱們的線程。併發
協程是單線程下的併發,協程是一種用戶態的輕量級線程,即協程是由用戶程序本身控制調度的。socket
python的線程屬於內核級別的,即由操做系統控制調度,如單線程遇到io或執行時間過長就會被迫交出cpu執行權限,切換其餘線程運行函數
單線程內開啓協程,一旦遇到io,就會從應用程序級別(而非操做系統)控制切換,以此來提高效率。spa
協程的切換開銷更小,屬於程序級別的切換,操做系統徹底感知不到,於是更加輕量級;單線程內就能夠實現併發的效果,最大限度地利用cpu操作系統
協程指的是單個線程,於是一旦協程出現阻塞,將會阻塞整個線程;線程
協程的本質是單線程下,沒法利用多核,能夠是一個程序開啓多個進程,每一個進程內開啓多個線程,每一個線程內開啓協程。code
greenlet模塊實現程序間的切換,缺點:遇到IO就阻塞server
greenlet只是提供了一種比generator更加便捷的切換方式,當切到一個任務執行時若是遇到io,那就原地阻塞,仍然是沒有解決遇到IO自動切換來提高效率的問題。協程
from greenlet import greenlet def eat(name): print('%s eat 1' %name) g2.switch('egon') print('%s eat 2' %name) g2.switch() def play(name): print('%s play 1' %name) g1.switch() print('%s play 2' %name) g1=greenlet(eat) g2=greenlet(play) g1.switch('egon') #能夠在第一次switch時傳入參數,之後都不須要
實現遇到gevent的IO自動切換
import gevent def eat(name): print('%s eat 1' %name) gevent.sleep(2) # 注意使用的是gevent.sleep(),而不是 time.sleep() print('%s eat 2' %name) def play(name): print('%s play 1' %name) gevent.sleep(1) print('%s play 2' %name) g1=gevent.spawn(eat,'egon') g2=gevent.spawn(play,name='egon') g1.join() # #者gevent.joinall([g1, g2]) g2.join() print('主')
猴子補丁實現全部IO的自動切換
from gevent import monkey;monkey.patch_all() import gevent import time def eat(): print('eat food 1') time.sleep(2) # time.sleep() print('eat food 2') def play(): print('play 1') time.sleep(1) print('play 2') g1=gevent.spawn(eat) g2=gevent.spawn(play_phone) gevent.joinall([g1, g2]) print('主')
from gevent import spawn, monkey;monkey.patch_all() import socket def communication(conn): while True: try: data = conn.recv(1024) if len(data) == 0: break conn.send(data.upper()) except ConnectionResetError as e: print(e) break conn.close() def server(ip, port): server = socket.socket() server.bind((ip, port)) server.listen(5) while True: conn, addr = server.accept() spawn(communication, conn) # 檢測IO, communication if __name__ == '__main__': g1 = spawn(server, '127.0.0.1', 8080) # 檢測IO,server g1.join() # 等待g1運行結束,即一直在循環檢測io