標籤:eve 高度 targe start [] span put cal send
一.線程隊列
隊列:
1.Queue
先進先出
內建鎖 資料安全
from queue import Queue
from multiprocessing import Queue (IPC隊列)
2.LifoQueue後進先出
後進先出
內建鎖 資料安全
from queue import LifoQueue lq=LifoQueue(5) lq.put(123) lq.put(666) lq.put(888) lq.put(999) lq.put("love") print(lq.put_nowait("miss")) #報錯 queue.Full print(lq) # <queue.LifoQueue object at 0x0000017901BC8C88> print(lq.get()) #love print(lq.get()) #999 print(lq.get()) #888 print(lq.get()) #666 print(lq.get()) #123 #print(lq.get_nowait()) #報錯 queue.Empty
3.PriorityQueue優先順序隊列
(放元組,數字從小到大,英文字母按ASCII碼先後順序)
from queue import PriorityQueue pq=PriorityQueue(4) pq.put((10,"aaa")) pq.put((5,"S")) pq.put((5,"ccc")) pq.put((10,"zzz")) #pq.put_nowait((10,"bbb")) #報錯queue.Full print(pq) # <queue.PriorityQueue object at 0x000001D6FEF38C50> print(pq.get()) print(pq.get()) #(5, ‘ccc‘) print(pq.get()) #(10, ‘aaa‘) print(pq.get()) #(10, ‘zzz‘) print(pq.get()) #(20, ‘bbb‘) # print(pq.get_nowait()) # 報錯queue.Empty
二 線程池
Multiprocessing模組 內建進程池Pool
Threading 模組 沒有Pool(沒有線程池)
concurrent.futures協助你管理線程池和進程池
高度封裝
進程池/線程池的統一的統一的使用方法
import time from threading import currentThread from concurrent.futures import ProcessPoolExecutor from concurrent.futures import ThreadPoolExecutor def func(i): time.sleep(1) print("in %s %s"%(i,currentThread())) return i**2 def back(fn): print(fn.result(),currentThread()) t=ThreadPoolExecutor(5) ret_l=[] for i in range(20): ret=t.submit(func,i).add_done_callback(back) # ret_l.append(ret) t.shutdown(wait=True) #括弧裡可以省略 # for ret in ret_l: # print(ret.result()) print(666)
ThreadPoolExecutor的相關方法:
1.t.map方法 啟動多線程任務 # t.map(func,range(20)) 替代for submit
2.t.submit(func,*args,**kwargs) 非同步提交任務
3.t.shutdown (wait=True) 相當於進程池的pool.close()+pool.join()操作 同步控制
wait=True,等待池內所有任務執行完畢回收完資源後才繼續
wait=False,立即返回,並不會等待池內的任務執行完畢
submit和map必須在shutdown之前
4.result擷取結果 ret.result()
5.回呼函數 add_done_callback(back)
在回呼函數內接收的參數是一個對象,需要通過result來擷取傳回值
在主進程中執行
三.協程
進程:資源分派的最小單位
線程 :CPU調度的最小單位
協程: 能在一條線程的基礎上,在多個任務之間互相切換
節省線程開啟的消耗
從python代碼的層級調度
正常的線程是CPU調度的最小單位
協程的調度並不是由作業系統來完成的.
(一).yield的機制就是協程
def func(): print(1) x=yield "aaa" print(x) yield "bbb" g=func() print(next(g)) print(g.send("***"))
(二).在多個函數之間互相切換的功能--協程
def consumer(): while True: x=yield print(x) def producer(): g=consumer() next(g) for i in range(10): g.send(i) producer()
yeild 只有程式之間的切換,沒有重利用任何IO操作的時間
greenlet(第三方模組) 程式環境切換
cmd : pip3 install 模組名 安裝第三方模組
(三).greenlet
協程模組 單純的程式切換耗費時間
import time from greenlet import greenlet def eat(): print(‘吃‘) time.sleep(1) g2.switch() print("吃完了") time.sleep(1) g2.switch() def play(): print("玩") time.sleep(1) g1.switch() print("玩美了") g1=greenlet(eat) g2=greenlet(play) g1.switch()
(四).gevent
遇到IO就切換 使用協程減少IO操作帶來的時間消耗
greenlet 是gevent的底層
gevent是基於greenlet實現的
python代碼在控製程序的切換
第一版:
import time import gevent from gevent import monkey def eat(): print("吃") gevent.sleep(2) print("吃完了") def play(): print("玩") gevent.sleep(2) print("玩美了") g1=gevent.spawn(eat) g2=gevent.spawn(play) g1.join() #等待g1結束 g2.join() #等待g2結束
第二版
要用gevent,需要將from gevent import monkey;monkey.patch_all()放到檔案的開頭
from gevent import monkey;monkey.patch_all() import time import gevent def eat(name): print("吃") time.sleep(2) print("%s吃完了"%name) def play(): print("玩") time.sleep(2) print("玩美了") g1=gevent.spawn(eat,"alex") #括弧裡傳參第一個是函數名,後面可以跟多個參數可以是位置參數,也可以是關鍵字參數,都是傳給eat的 g2=gevent.spawn(play) gevent.joinall([g1,g2])# g1.join()和g2.join()合并成一個. print(g1.value) #None
四.協程起socket(tcp)
伺服器代碼
from gevent import monkey;monkey.patch_all()import socketimport geventdef talk(conn): while True: conn.send(b‘hallo‘) print(conn.recv(1024))sk=socket.socket()sk.bind(("127.0.0.1",9902))sk.listen()while True: conn,addr=sk.accept() gevent.spawn(talk,conn)
用戶端代碼
import socketfrom threading import Threaddef client(): sk=socket.socket() sk.connect(("127.0.0.1",9902)) while True: print(sk.recv(1024)) sk.send(b‘hi‘)for i in range(5): Thread(target=client).start()
python全棧開發 * 線程隊列 線程池 協程 * 180731