一、初識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什麼的, 還是自己先看書學好概念吧~