python併發,協程
阿新 • • 發佈:2018-12-12
在作業系統中程序是資源分配的最小單位, 執行緒是CPU排程的最小單位。
協程:是單執行緒下的併發,又稱微執行緒,纖程。英文名Coroutine。一句話說明:協程是一種使用者態的輕量級執行緒,即協程是由使用者程式自己控制排程的。也就是說程式設計師用程式碼來控制切換.
參考: http://www.cnblogs.com/Eva-J/articles/8324673.html
# 程序 啟動多個程序 程序之間是由作業系統負責呼叫 # 執行緒 啟動多個執行緒 真正被CPU執行的最小單位實際是執行緒 # 開啟一個執行緒 建立一個執行緒 暫存器 堆疊 # 關閉一個執行緒 # 協程 # 本質上是一個執行緒 # 能夠在多個任務之間切換來節省一些IO時間 # 協程中任務之間的切換也消耗時間,但是開銷要遠遠小於程序執行緒之間的切換 # 實現併發的手段 import time def consumer(): while True: x = yield time.sleep(1) print('處理資料 :',x) def producer(): c = consumer() next(c) for i in range(10): time.sleep(1) print('生產資料:',i) c.send(i) # 這個生產者消費者模型 模擬了程式的來回切換, 但是不能規避IO時間 producer()
使用pip3 install greenlet 和 pip3 install gevent 安裝好模組,繼續:
# 真正的協程模組就是使用greenlet完成的切換 from greenlet import greenlet def eat(): print('eating start') g2.switch() # 切換到g2 print('eating end') g2.switch() def play(): print('playing start') g1.switch() # 切換到g1 print('playing end') g1 = greenlet(eat) # 委託給g1 g2 = greenlet(play) g1.switch()
- greenlet可以實現協程,不過每一次都要人為的去指向下一個該執行的協程,顯得太過麻煩。python還有一個比greenlet更強大的並且能夠自動切換任務的模組gevent
參考:https://www.cnblogs.com/PrettyTom/p/6628569.html
# 協程是一種使用者態的輕量級執行緒,即協程是由使用者程式自己控制排程的。 import time import gevent def eat(): print('eating start') # time.sleep(1) # gevent 不能感知到time.sleep時間 gevent.sleep(1) print('eating end') def play(): print('playing start') gevent.sleep(1) print('playing end') g1 = gevent.spawn(eat) g2 = gevent.spawn(play) g1.join() g2.join()
gevent的正確方式:
## 匯入這句,將所有模組中的阻塞IO都打成一個包。就可以感知 time.sleep from gevent import monkey;monkey.patch_all() import time import gevent import threading def eat(): print(threading.current_thread().getName()) # Dummy 假的,虛擬的。 print(threading.current_thread()) print('eating start') time.sleep(1.2) print('eating end') def play(): print(threading.current_thread().getName()) print(threading.current_thread()) print('playing start') time.sleep(1) print('playing end') g1 = gevent.spawn(eat) # 註冊到協程,遇到IO將自動切換 g2 = gevent.spawn(play) # g1.join() # g2.join() gevent.joinall([g1,g2]) print('master') # 程序和執行緒的任務切換由作業系統完成 # 協程任務之間的切換由程式(程式碼)完成,只有遇到協程模組能識別的IO操作的時候,程式才會進行任務切換,實現併發的效果
同步和非同步:
# 同步 和 非同步 from gevent import monkey;monkey.patch_all() import time import gevent def task(n): time.sleep(1) print(n) def sync(): for i in range(5): task(i) def async(): g_lst = [] for i in range(5): g = gevent.spawn(task,i) g_lst.append(g) gevent.joinall(g_lst) # for g in g_lst:g.join() sync() # 同步 async() # 非同步
爬蟲時使用協程併發
# 協程 : 能夠在一個執行緒中實現併發效果的概念 # 能夠規避一些任務中的IO操作 # 在任務的執行過程中,檢測到IO就切換到其他任務 # 多執行緒 被弱化了 # 協程 在一個執行緒上 提高CPU 的利用率 # 協程相比於多執行緒的優勢 切換的效率更快 # 爬蟲的例子 # 請求過程中的IO等待 from gevent import monkey;monkey.patch_all() import gevent from urllib.request import urlopen # 內建的模組 def get_url(url): response = urlopen(url) content = response.read().decode('utf-8') return len(content) g1 = gevent.spawn(get_url,'http://www.baidu.com') g2 = gevent.spawn(get_url,'http://www.sogou.com') g3 = gevent.spawn(get_url,'http://www.taobao.com') g4 = gevent.spawn(get_url,'http://www.hao123.com') g5 = gevent.spawn(get_url,'http://www.cnblogs.com') gevent.joinall([g1,g2,g3,g4,g5]) print(g1.value) print(g2.value) print(g3.value) print(g4.value) print(g5.value) ret = get_url('http://www.baidu.com') print(ret)