python-協程
阿新 • • 發佈:2018-05-29
mic input atomic 控制 高並發 3.1 並發處理 日常 第三方庫
1.協程
協程,又稱微線程,纖程。英文名Coroutine。一句話說明什麽是線程:協程是一種用戶態的輕量級線程。
協程擁有自己的寄存器上下文和棧。協程調度切換時,將寄存器上下文和棧保存到其他地方,在切回來的時候,恢復先前保存的寄存器上下文和棧。因此:
協程能保留上一次調用時的狀態(即所有局部狀態的一個特定組合),每次過程重入時,就相當於進入上一次調用的狀態,換種說法:進入上一次離開時所處邏輯流的位置。
2.協程的優點缺點。
協程的好處: 無需線程上下文切換的開銷 無需原子操作鎖定及同步的開銷 "原子操作(atomic operation)是不需要synchronized",所謂原子操作是指不會被線程調度機制打斷的操作;這種操作一旦開始,就一直運行到結束,中間不會有任何 context switch (切換到另一個線程)。
原子操作可以是一個步驟,也可以是多個操作步驟,但是其順序是不可以被打亂,或者切割掉只執行部分。視作整體是原子性的核心。 方便切換控制流,簡化編程模型 高並發+高擴展性+低成本:一個CPU支持上萬的協程都不是問題。所以很適合用於高並發處理。 缺點: 無法利用多核資源:協程的本質是個單線程,它不能同時將 單個CPU 的多個核用上,協程需要和進程配合才能運行在多CPU上.當然我們日常所編寫的絕大部分應用都沒有這個必要,除非是cpu密集型應用。 進行阻塞(Blocking)操作(如IO時)會阻塞掉整個程序
3.python的greenlet模塊。
greenlet是一個用C實現的協程模塊,相比與python自帶的yield,它可以使你在任意函數之間隨意切換,而不需把這個函數先聲明為generato
手動切換。
3.1 greenlet的簡單實例。
def t1(): print(11) gr2.switch() print(33) gr2.switch() def t2(): print(22) gr1.switch() print(44) gr1 = greenlet(t1) gr2 = greenlet(t2) gr1.switch()
4.python的Gevent模塊。
Gevent 是一個第三方庫,可以輕松通過gevent實現並發同步或異步編程,在gevent中用到的主要模式是Greenlet, 它是以C擴展模塊形式接入Python的輕量級協程。 Greenlet全部運行在主程序操作系統進程的內部,但它們被協作式地調度。
4.1 gevent的簡單實例
import gevent,time def t1(): print(11) gevent.sleep(1) print(33) def t2(): print(22) gevent.sleep(3) print(44) start_time = time.time() gevent.joinall([ gevent.spawn(t1), gevent.spawn(t2), ]) print(‘cost:{0}‘.format(time.time() - start_time))
5.協程實現一個簡單爬蟲 並對比,串行和並行的執行時間。
import gevent,time,requests from gevent import monkey """ 把當前程序的所有IO操作,標記上記號。 讓gevent知道程序中IO操作的結束時間。 """ monkey.patch_all() def get1(url): print(‘get url : {0}‘.format(url)) rep = requests.get(url) rep.encoding = ‘utf-8‘ print(rep) start_time = time.time() url_lists = [ ‘http://www.gec-edu.org/‘, ‘http://www.baidu.com/‘, ‘http://www.sina.com.cn/‘, ] for item in url_lists: get1(item) # gevent.joinall() print(‘cost:{0}‘.format(time.time() - start_time)) aync_start_time = time.time() gevent.joinall([ gevent.spawn(get1(url_lists[0])), gevent.spawn(get1(url_lists[1])), gevent.spawn(get1(url_lists[2])), ]) print(‘aync cost:{0}‘.format(time.time() - aync_start_time))
6.利用協程實現socket多並發。
6.1 client.py
import gevent,socket,time def run(): client() def client(): client = socket.socket() client.connect((‘127.0.0.1‘, 8089)) while True: input_value = input(">>:").strip() if not input_value: continue client.send(input_value.encode(‘utf-8‘)) rev_data = client.recv(1024).decode(‘utf-8‘) print(rev_data) continue if __name__ == ‘__main__‘: run()
6.2 servier.py
import gevent,socket,time from gevent import monkey """ 利用gevent實現socket服務端的多並發。 思路:每一請求切換一個協程處理 """ monkey.patch_all() def server(): server = socket.socket() # 綁定監聽端口 server.bind((‘127.0.0.1‘, 8089)) # 監聽 server.listen() while True: connect,address = server.accept() print(‘server listen request...‘,address) """ 如果進來一個請求,則交由一個協程處理 """ gevent.spawn(handle_request,connect,address) def handle_request(conncet,address): try: while True: rev_data = conncet.recv(1024).decode(‘utf-8‘) if not rev_data: break print(address,‘server recv : {0}‘.format(rev_data)) conncet.send(‘recv ok! request data : {0}‘.format(rev_data).encode(‘utf-8‘)) except Exception as e: print(e) finally: conncet.close() def run(): server() if __name__ == ‘__main__‘: run()
python-協程