(轉)有關Queue隊列


Queue

Queue是python標准庫中的線程安全的隊列(FIFO)實現,提供了一個適用於多線程編程的先進先出的數據結構,即隊列,用來在生產者和消費者線程之間的信息傳遞

基本FIFO隊列

class Queue.Queue(maxsize=0)

FIFO即First in First Out,先進先出。Queue提供了一個基本的FIFO容器,使用方法很簡單,maxsize是個整數,指明了隊列中能存放的數據個數的上限。一旦達到上限,插入會導致阻塞,直到隊列中的數據被消費掉。如果maxsize小於或者等於0,隊列大小沒有限制。

舉個例子:

 1 import Queue
 2 
 3 q = Queue.Queue()
 4 
 5 for i in range(5):
 6     q.put(i)
 7 
 8 while not q.empty():
 9     print q.get()
10 
11 輸出結果:
12 0
13 1
14 2
15 3
16 4

LIFO隊列

class Queue.LifoQueue(maxsize=0)

LIFO即Last in First Out,后進先出。與棧的類似,使用也很簡單,maxsize用法同上

舉個例子:

 1 import Queue
 2 
 3 q = Queue.LifoQueue()
 4 
 5 for i in range(5):
 6     q.put(i)
 7 
 8 while not q.empty():
 9     print q.get()
10 
11 輸出:
12 
13 4
14 3
15 2
16 1
17 0

可以看到僅僅是將Queue.Quenu類替換為Queue.LifiQueue類

 

優先級隊列

class Queue.PriorityQueue(maxsize=0)

構造一個優先隊列。maxsize用法同上。

import Queue
import threading

class Job(object):
    def __init__(self, priority, description):
        self.priority = priority
        self.description = description
        print 'Job:',description
        return
    def __cmp__(self, other):
        return cmp(self.priority, other.priority)

q = Queue.PriorityQueue()

q.put(Job(3, 'level 3 job'))
q.put(Job(10, 'level 10 job'))
q.put(Job(1, 'level 1 job'))

def process_job(q):
    while True:
        next_job = q.get()
        print 'for:', next_job.description
        q.task_done()

workers = [threading.Thread(target=process_job, args=(q,)),
        threading.Thread(target=process_job, args=(q,))
        ]

for w in workers:
    w.setDaemon(True)
    w.start()

q.join()

輸出結果:

Job: level 3 job
Job: level 10 job
Job: level 1 job
for: level 1 job
for: level 3 job
for: job: level 10 job

一些常用方法

task_done()

意味着之前入隊的一個任務已經完成。由隊列的消費者線程調用。每一個get()調用得到一個任務,接下來的task_done()調用告訴隊列該任務已經處理完畢。

如果當前一個join()正在阻塞,它將在隊列中的所有任務都處理完時恢復執行(即每一個由put()調用入隊的任務都有一個對應的task_done()調用)。

join()

阻塞調用線程,直到隊列中的所有任務被處理掉。

只要有數據被加入隊列,未完成的任務數就會增加。當消費者線程調用task_done()(意味着有消費者取得任務並完成任務),未完成的任務數就會減少。當未完成的任務數降到0,join()解除阻塞。

put(item[, block[, timeout]])

將item放入隊列中。

  1. 如果可選的參數block為True且timeout為空對象(默認的情況,阻塞調用,無超時)。
  2. 如果timeout是個正整數,阻塞調用進程最多timeout秒,如果一直無空空間可用,拋出Full異常(帶超時的阻塞調用)。
  3. 如果block為False,如果有空閑空間可用將數據放入隊列,否則立即拋出Full異常

其非阻塞版本為put_nowait等同於put(item, False)

get([block[, timeout]])

從隊列中移除並返回一個數據。block跟timeout參數同put方法

其非阻塞方法為`get_nowait()`相當與get(False)

empty()

如果隊列為空,返回True,反之返回False

 

==================================================================================================

創建一個“隊列”對象

import Queue
myqueue = Queue.Queue(maxsize = 10)

Queue.Queue類即是一個隊列的同步實現。隊列長度可為無限或者有限。可通過Queue的構造函數的可選參數maxsize來設定隊列長度。如果maxsize小於1就表示隊列長度無限。

 

將一個值放入隊列中

myqueue.put(10)

調用隊列對象的put()方法在隊尾插入一個項目。put()有兩個參數,第一個item為必需的,為插入項目的值;第二個block為可選參數,默認為1。如果隊列當前為空且block為1,put()方法就使調用線程暫停,直到空出一個數據單元。如果block為0,put方法將引發Full異常。

PS:block參數為True,相當於隊伍排滿了,就在旁邊等着,騰出位置再插進去;block為False,相當於隊伍排滿了,沒位置了,直接投訴,不他媽跟你墨跡

 

將一個值從隊列中取出

myqueue.get()

調用隊列對象的get()方法從隊頭刪除並返回一個項目。可選參數為block,默認為True。如果隊列為空且block為True,get()就使調用線程暫停,直至有項目可用。如果隊列為空且block為False,隊列將引發Empty異常。

PS:block參數為True,相當於鍋里沒吃的了,就在旁邊等着上新菜,上了新菜再直接端走;block為False,相當於鍋里沒貨了,沒耐心等新菜上鍋,直接掀鍋砸灶,去你媽的

 

python queue模塊有三種隊列:
1、python queue模塊的FIFO隊列先進先出。
2、LIFO類似於堆。即先進后出。
3、還有一種是優先級隊列級別越低越先出來。 

針對這三種隊列分別有三個構造函數:
1、class Queue.Queue(maxsize) FIFO 
2、class Queue.LifoQueue(maxsize) LIFO 
3、class Queue.PriorityQueue(maxsize) 優先級隊列 

介紹一下此包中的常用方法:

Queue.qsize() 返回隊列的大小 
Queue.empty() 如果隊列為空,返回True,反之False 
Queue.full() 如果隊列滿了,返回True,反之False
Queue.full 與 maxsize 大小對應 
Queue.get([block[, timeout]])獲取隊列,timeout等待時間 
Queue.get_nowait() 相當Queue.get(False)
非阻塞 Queue.put(item) 寫入隊列,timeout等待時間 
Queue.put_nowait(item) 相當Queue.put(item, False)
Queue.task_done() 在完成一項工作之后,Queue.task_done()函數向任務已經完成的隊列發送一個信號
Queue.join() 實際上意味着等到隊列為空,再執行別的操作

 

一些需要注意的地方:

1. 阻塞模式

import Queue

q = Queue.Queue(10)

......
       for i in range(10):
               q.put('A')
               time.sleep(0.5)

這是一段極其簡單的代碼(另有兩個線程也在操作隊列q),我期望每隔0.5秒寫一個'A'到隊列中,但總是不能如願:間隔時間有時會遠遠超過0.5秒。原來,Queue.put()默認有 block = True 和 timeou 兩個參數。當  block = True 時,寫入是阻塞式的,阻塞時間由 timeou  確定。當隊列q被(其他線程)寫滿后,這段代碼就會阻塞,直至其他線程取走數據。Queue.put()方法加上 block=False 的參數,即可解決這個隱蔽的問題。但要注意,非阻塞方式寫隊列,當隊列滿時會拋出 exception Queue.Full 的異常。

PS:如果沒能及時put,說明隊伍滿了,還他媽在排隊呢,且等着

 

2. 無法捕獲 exception Queue.Empty 的異常

while True:
                ......
                try:
                        data = q.get()
                except Queue.Empty:
                        break

我的本意是用隊列為空時,退出循環,但實際運行起來,卻陷入了死循環。這個問題和上面有點類似:Queue.get()默認的也是阻塞方式讀取數據,隊列為空時,不會拋出 except Queue.Empty ,而是進入阻塞直至超時。 加上block=False 的參數,問題迎刃而解。

PS:老子還在排隊等上菜,你拋錘子的異常,有本事直接砸鍋掀灶台,否則乖乖一邊站着等

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM