隊列(Queue)
在多個線程之間安全的交換數據信息,隊列在多線程編程中特別有用
隊列的好處:
- 提高雙方的效率,你只需要把數據放到隊列中,中間去干別的事情。
- 完成了程序的解耦性,兩者關系依賴性沒有不大。
一、隊列的類型:
1、lass queue.Queue(maxsize=0)
先進先出,后進后出
import queue q = queue.Queue() # 生成先入先出隊列實例 q.put(1) # 先放進1,再放入2 q.put(2) print(q.get()) # # 輸出 1
2、class queue.LifoQueue(maxsize=0)
是先進后出,后進新出規則,last in fisrt out
import queue q = queue.LifoQueue() # 生成后入先出隊列實例 q.put(1) # 先放進1,再放入2 q.put(2) print(q.get()) # # 輸出 2
3、class queue.PriorityQueue(maxsize=0)
根據優先級來取數據。存放數據的格式 : Queue.put((priority_number,data)),priority_number越小,優先級越高,data代表存入的值
import queue q = queue.PriorityQueue() q.put((1, "d1")) q.put((-1, "d2")) q.put((6, "d3")) print(q.get()) print(q.get()) print(q.get()) #執行結果 (-1, 'd2') (1, 'd1') (6, 'd3')
注:maxsize代表這個隊列最大能夠put的長度
二、隊列(Queue)的內置方法
1、exception queue.Empty 當隊列中的數據為空時,就會拋出這個異常。 >>> import queue >>> q = queue.Queue() >>> q.get(block=False) #獲取不到的時候 Traceback (most recent call last): File "<input>", line 1, in <module> File "D:\Python\Python35\lib\queue.py", line 161, in get raise Empty queue.Empty ############################################### 2、 exception queue.Full 當隊列中滿了以后,再放數據的話,就會拋出此異常。 >>> import queue >>> q = queue.Queue(maxsize=1) #創建隊列實例,並且設置最大值為1 >>> q.put(1) >>> q.put(1,block=False) Traceback (most recent call last): File "<input>", line 1, in <module> File "D:\Python\Python35\lib\queue.py", line 130, in put raise Full queue.Full ############################################### 3、Queue.qsize() 查看隊列的大小 >>> import queue >>> q = queue.Queue() >>> q.put(1) >>> q.qsize() #查看隊列的大小 1 ############################################### 4、Queue.empty() 隊列如果為空返回True,不為空返回False >>> import queue >>> q = queue.Queue() >>> q.put(1) >>> q.empty() #隊列不為空 False >>> q.get() 1 >>> q.empty() #隊列為空 True ############################################### 5、Queue.full() 隊列如果滿了,返回True,沒有滿返回False >>> import queue >>> q = queue.Queue(maxsize=1) #設置隊列的大小為1 >>> q.full() #隊列沒有滿 False >>> q.put(1) >>> q.full() #隊列已滿 True ############################################### 6、Queue.put(item,block=True,timeout=None) 把數據插入隊列中。block參數默認為true,timeout默認值是None。如果blcok為false的話,那么在put時候超過設定的maxsize的值,就會報full 異常。如果timeout設置值得話,說明put值得個數超過maxsize值,那么會在timeout幾秒之后拋出full異常。 >>> import queue >>> q = queue.Queue(maxsize=1) #是定隊列的大小為1 >>> q.put(1) >>> q.put(1,block=False) #block不會阻塞,會full異常 Traceback (most recent call last): File "<input>", line 1, in <module> File "D:\Python\Python35\lib\queue.py", line 130, in put raise Full queue.Full >>> q.put(1,timeout=1) #超過1秒,則會報full異常 Traceback (most recent call last): File "<input>", line 1, in <module> File "D:\Python\Python35\lib\queue.py", line 141, in put raise Full queue.Full ############################################### 7、Queue.put_nowait(item) 這個其實等同於Queue.put(item,block=False)或者是Queue.put(item,False) >>> import queue >>> q = queue.Queue(maxsize=1) >>> q.put(1) >>> q.put_nowait(1) #等同於q.put(1,block=False) Traceback (most recent call last): File "<input>", line 1, in <module> File "D:\Python\Python35\lib\queue.py", line 184, in put_nowait return self.put(item, block=False) File "D:\Python\Python35\lib\queue.py", line 130, in put raise Full queue.Full ############################################### 8、Queue.get(block=True,timeout=None) 移除並返回隊列中的序列。參數block=true並且timeout=None。如果block=false的話,那么隊列為空的情況下,就直接Empty異常。如果timeout有實際的值,這個時候隊列為空,執行get的時候,則時隔多長時間則報出Empty的異常。 >>> import queue >>> q = queue.Queue() >>> q.put(1) >>> q.get() 1 >>> q.get(block=False) #獲取不到值,直接拋Empty異常 Traceback (most recent call last): File "<input>", line 1, in <module> File "D:\Python\Python35\lib\queue.py", line 161, in get raise Empty queue.Empty >>> q.get(timeout=1) #設置超時時間,拋出Empty異常 Traceback (most recent call last): File "<input>", line 1, in <module> File "D:\Python\Python35\lib\queue.py", line 172, in get raise Empty queue.Empty ############################################### 9、Queue.get_nowait(item) 其實這個等同於Queue.get(block=False)或者Queue.get(False) >>> import queue >>> q = queue.Queue() >>> q.put(1) >>> q.get() 1 >>> q.get_nowait() #等同於q.get(block=False) Traceback (most recent call last): File "<input>", line 1, in <module> File "D:\Python\Python35\lib\queue.py", line 192, in get_nowait return self.get(block=False) File "D:\Python\Python35\lib\queue.py", line 161, in get raise Empty queue.Empty ############################################### 10、Queue.task_done() get()用於獲取任務,task_done()則是用來告訴隊列之前獲取的任務已經處理完成 ############################################### 11、Queue.join() block(阻塞)直到queue(隊列)被消費完畢 如果生產者生產10個包子,那么要等消費者把這個10個包子全部消費完畢,生產者才能繼續往下執行。
生產者消費者模型
並發編程中使用生產者和消費者模式能夠解決絕大多數並發問題。該模式通過平衡生產線程和消費線程的工作能力來提高程序的整體處理數據的速度。
1、為什么要使用生產者和消費者模式
在線程世界里,生產者就是生產數據的線程,消費者就是消費數據的線程。在多線程開發當中,如果生產者處理速度很快,而消費者處理速度很慢,那么生產者就必須等待消費者處理完,才能繼續生產數據。同樣的道理,如果消費者的處理能力大於生產者,那么消費者就必須等待生產者。為了解決這個問題於是引入了生產者和消費者模式。
2、什么是生產者消費者模式
生產者消費者模式是通過一個容器來解決生產者和消費者的強耦合問題。生產者和消費者彼此之間不直接通訊,而通過阻塞隊列來進行通訊,所以生產者生產完數據之后不用等待消費者處理,直接扔給阻塞隊列,消費者不找生產者要數據,而是直接從阻塞隊列里取,阻塞隊列就相當於一個緩沖區,平衡了生產者和消費者的處理能力。
3、生成者消費者模型例子
3.1、生產者生產完畢,消費者再消費例子:
import threading import queue def producer(): """ 模擬生產者 :return: """ for i in range(10): q.put("骨頭 %s" % i) print("開始等待所有的骨頭被取走...") q.join() # 等待這個骨頭隊列被消費完畢 print("所有的骨頭被取完了...") def consumer(n): """ 模擬消費者 :return: """ while q.qsize() > 0: print("%s 取到" % n, q.get()) q.task_done() # 每去到一個骨頭,便告知隊列這個任務執行完了 q = queue.Queue() p = threading.Thread(target=producer,) p.start() c1 = consumer("QQ")
3.2 邊生產邊消費的模型例子
import time,random import queue,threading q = queue.Queue() def producer(name): count = 0 while count < 20: time.sleep(random.randrange(3)) q.put(count) # 在隊列里放包子 print('Producer %s has produced %s baozi..' % (name, count)) count += 1 def consumer(name): count = 0 while count < 20: time.sleep(random.randrange(4)) if not q.empty(): # 如果還有包子 data = q.get() # 就繼續獲取保證 print(data) print('\033[32;1mConsumer %s has eat %s baozi...\033[0m' % (name, data)) else: print("-----no baozi anymore----") count += 1 p1 = threading.Thread(target=producer, args=('A',)) c1 = threading.Thread(target=consumer, args=('B',)) p1.start() c1.start()
3.3、流程圖
圖解:
- 生產者生產,消費者消費。
- 消費者每消費一次,都要去執行以下task_done()方法,來告訴消費者已經消費成功,相當於吃完飯,消費者應該給錢了。
- 消費者每消費一次,則隊列中計數器會做減1操作。
- 當隊列中的計數器為0的時候,則生產者不阻塞,繼續執行,不為0的時候,則阻塞,直到消費者消費完畢為止。