python隊列模組Queue用法詳解

來源:互聯網
上載者:User


一、初識Queue模組

Queue模組實現了多生產者、多消費者隊列。它特別適用於資訊必須在多個線程間安全地交換的多線程程式中。這個模組中的Queue類實現了所有必須的鎖語義。它依賴於Python中線程支援的可用性;參見threading模組。

模組實現了三類隊列:FIFO(First In First Out,先進先出,預設為該隊列)、LIFO(Last In First Out,後進先出)、基於優先順序的隊列。以下為其常用方法:

先進先出  q = Queue.Queue(maxsize)
後進先出  a = Queue.LifoQueue(maxsize)
優先順序  Queue.PriorityQueue(maxsize)
Queue.qsize() 返回隊列的大小
Queue.empty() 如果隊列為空白,返回True,反之False
Queue.full() 如果隊列滿了,返回True,反之False
Queue.full 與 maxsize 大小對應
Queue.put(item) 寫入隊列,timeout等待時間   非阻塞
Queue.get([block[, timeout]]) 擷取隊列,timeout等待時間
Queue.get_nowait() 相當Queue.get(False)
Queue.put_nowait(item) 相當Queue.put(item, False)
Queue.task_done() 在完成一項工作之後,函數向任務已經完成的隊列發送一個訊號
Queue.join(): 實際上意味著等到隊列為空白,再執行別的操作
更詳細部分可以參看python標準庫之Queue模組介紹。

二、隊列示列

1、FIFO(先進先出)

import Queue
q = Queue.Queue()
for i in range(5):
    q.put(i)
while not q.empty():
    print q.get()
其輸出結果如下:

[root@361way queue]# python fifo.py

0
1
2
3
4

其輸出順序與進入順序相同。

2、LIFO(後進先出)

import Queue
q = Queue.LifoQueue()
for i in range(5):
    q.put(i)
while not q.empty():
    print q.get()
執行結果如下:

import Queue
q = Queue.LifoQueue()
for i in range(5):
    q.put(i)
while not q.empty():
    print q.get()

3、帶優先順序的隊列

import Queue
class Job(object):
    def __init__(self, priority, description):
        self.priority = priority
        self.description = description
        print 'New job:', description
        return
    def __cmp__(self, other):
        return cmp(self.priority, other.priority)
q = Queue.PriorityQueue()
q.put( Job(3, 'Mid-level job') )
q.put( Job(10, 'Low-level job') )
q.put( Job(1, 'Important job') )
while not q.empty():
    next_job = q.get()
    print 'Processing job:', next_job.description

執行結果如下:

[root@361way queue]# python Queue_priority.py
New job: Mid-level job
New job: Low-level job
New job: Important job
Processing job: Important job
Processing job: Mid-level job
Processing job: Low-level job
從上面的執行結果可以看出,優先順序值設定越小,越先執行。另外這裡是以單線程為例的,在多thread的樣本中,多個線程同時get()  item 時,這時就可以根據優先順序決定哪一個任務先執行。

三、隊列與線程

在實際使用隊列是與線程結合在一起的。這裡列幾個隊列與線程的程式碼範例:

from Queue import *
from threading import Thread
import sys
'''this function will process the items in the queue, in serial'''
def processor():
    while True:
        if queue.empty() == True:
            print "the Queue is empty!"
            sys.exit(1)
        try:
            job = queue.get()
            print "I'm operating on job item: %s"%(job)
            queue.task_done()
        except:
            print "Failed to operate on job"
'''set variables'''
queue = Queue()
threads = 4
'''a list of job items. you would want this to be more advanced,
like reading from a file or database'''
jobs = [ "job1", "job2", "job3" ]
'''iterate over jobs and put each into the queue in sequence'''
#for job in jobs:
for job in range(100):
     print "inserting job into the queue: %s"%(job)
     queue.put(job)
'''start some threads, each one will process one job from the queue'''
#for i in range(100):
for i in range(threads):
     th = Thread(target=processor)
     th.setDaemon(True)
     th.start()
'''wait until all jobs are processed before quitting'''
queue.join()

需要注意的是processer函數裡的“ while True:”行 ,如果沒了這行,當線程(thread)數小於隊列數時,第一輪迴圈完後就會卡住,不執行後面的迴圈了。所以加上該行,就相當於開始了一個死迴圈,直到所有的隊列結束時,隊列為空白,迴圈結束。

樣本2:

[root@361way tmp]# python queue-example-1.py
task 0 finished
task 1 finished
task 3 finished
task 2 finished
task 5 finished
task 4 finished
task 6 finished
task 7 finished
task 9 finished
task 8 finished
[root@361way tmp]# more queue-example-1.py
# File: queue-example-1.py
import threading
import Queue
import time, random
WORKERS = 2
class Worker(threading.Thread):
    def __init__(self, queue):
        self.__queue = queue
        threading.Thread.__init__(self)
    def run(self):
        while 1:
            item = self.__queue.get()
            if item is None:
                break # reached end of queue
            # pretend we're doing something that takes 10-100 ms
            time.sleep(random.randint(10, 100) / 1000.0)
            print "task", item, "finished"
#
# try it
queue = Queue.Queue(0)
for i in range(WORKERS):
    Worker(queue).start() # start a worker
for i in range(10):
    queue.put(i)
for i in range(WORKERS):
    queue.put(None) # add end-of-queue markers

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() 實際上意味著等到隊列為空白,再執行別的操作
 
 
附上一個例子:

#coding:utf-8

import Queue
import threading
import time
import random

q = Queue.Queue(0) #當有多個線程共用一個東西的時候就可以用它了
NUM_WORKERS = 3

class MyThread(threading.Thread):

    def __init__(self,input,worktype):
       self._jobq = input
       self._work_type = worktype
       threading.Thread.__init__(self)

    def run(self):
       while True:
           if self._jobq.qsize() > 0:
               self._process_job(self._jobq.get(),self._work_type)
           else:break

    def _process_job(self, job, worktype):
       doJob(job,worktype)

def doJob(job, worktype):
   time.sleep(random.random() * 3)
    print"doing",job," worktype ",worktype

if __name__ == '__main__':
    print "begin...."
    for i inrange(NUM_WORKERS * 2):
       q.put(i) #放入到任務隊列中去
    print "job qsize:",q.qsize()

    for x inrange(NUM_WORKERS):
       MyThread(q,x).start()

python下多線程的思考

先來個例子:

 
import Queue,threading,time,random
 
 
 
class consumer(threading.Thread):
    def __init__(self,que):
        threading.Thread.__init__(self)
        self.daemon = False
        self.queue = que
    def run(self):
        while True:
            if self.queue.empty():
                break
            item = self.queue.get()
            #processing the item 
            time.sleep(item)
            print self.name,item
            self.queue.task_done()
        return
que = Queue.Queue()
for x in range(10):
    que.put(random.random() * 10, True, None)
consumers = [consumer(que) for x in range(3)]
 
for c in consumers:
    c.start()
que.join()
import Queue,threading,time,random

 

class consumer(threading.Thread):
    def __init__(self,que):
        threading.Thread.__init__(self)
        self.daemon = False
        self.queue = que
    def run(self):
        while True:
            if self.queue.empty():
                break
            item = self.queue.get()
            #processing the item
            time.sleep(item)
            print self.name,item
            self.queue.task_done()
        return
que = Queue.Queue()
for x in range(10):
    que.put(random.random() * 10, True, None)
consumers = [consumer(que) for x in range(3)]

for c in consumers:
    c.start()
que.join()

 

代碼的功能是產生10個隨機數(0~10範圍),sleep相應時間後輸出數字和線程名稱

這段代碼裡,是一個快速生產者(產生10個隨機數),3個慢速消費者的情況。

在這種情況下,先讓三個consumers跑起來,然後主線程用que.join()阻塞。

當三個線程發現隊列都空時,各自的run函數返回,三個線程結束。同時主線程的阻塞開啟,全部程式結束。

 
而對於慢速生產者和快速消費者而言,代碼如下:

 
import Queue,threading,time,random
 
 
class consumer(threading.Thread):
    def __init__(self,que):
        threading.Thread.__init__(self)
        self.daemon = False
        self.queue = que
    def run(self):
        while True:
            item = self.queue.get()
            if item == None:
                break
            #processing the item 
            print self.name,item
            self.queue.task_done()
        self.queue.task_done()
        return
que = Queue.Queue()
 
consumers = [consumer(que) for x in range(3)]
for c in consumers:
    c.start()
for x in range(10):
    item = random.random() * 10
    time.sleep(item)
    que.put(item, True, None)
 
 
que.put(None)
que.put(None)
que.put(None)
que.join()
import Queue,threading,time,random

 

class consumer(threading.Thread):
    def __init__(self,que):
        threading.Thread.__init__(self)
        self.daemon = False
        self.queue = que
    def run(self):
        while True:
            item = self.queue.get()
            if item == None:
                break
            #processing the item
            print self.name,item
            self.queue.task_done()
        self.queue.task_done()
        return
que = Queue.Queue()

consumers = [consumer(que) for x in range(3)]
for c in consumers:
    c.start()
for x in range(10):
    item = random.random() * 10
    time.sleep(item)
    que.put(item, True, None)


que.put(None)
que.put(None)
que.put(None)
que.join()

 

這種情況下,快速消費者在get時需要阻塞(否則返回了這線程就結束了~)因此對於停止整個程式,使用的是None標記,讓子線程遇到None便返回結束。

因為消費速度大於產生速度,因此先運行子線程等待隊列加入新的元素,然後再慢速地新增工作。

注意最後put(None)三次,是因為每個線程返回都會取出一個None,都要這樣做才可以使三個線程全部停止。當然有種更簡單粗暴的方法,就是把子線程設定為deamon,一但生產完成,開始que.join()阻塞直至隊列空就結束主線程,子線程雖然在阻塞等待隊列也會因為deamon屬性而被強制關閉。。。。

 
 

本文舉了2個單一生產者多消費者的例子。參考資料是<python參考手冊>第三版,上面有單c和單p的代碼。

有關多線程的函數如put,join什麼的, 還是自己先看書學好概念吧~

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.