目录
一、初始化函数:
二、 task_done(self):
三、join(self):
四、put(self, item, block=True, timeout=None):
五、阻塞的情况
怎么实现阻塞队列?当然是靠锁,但是应该怎么锁?一把锁能在not_empty,not_full,all_tasks_done三个条件之间共享。好比说,现在有线程A线程B,他们准备向队列put任务,队列的最大长度是5,线程A在put时,还没完事,线程B就开始put,队列就塞不下了,所以当线程A抢占到put权时应该加把锁,不让线程B对队列操作。鬼畜区经常看到的计数君,在线程中也同样重要,每次put完unfinished要加一,get完unfinished要减一。
import threading
import time
from collections import deque
class BlockQueue:
def __init__(self, maxsize=0):
self.mutex = threading.Lock()
self.maxsize = maxsize
self.not_full = threading.Condition(self.mutex)
self.not_empty = threading.Condition(self.mutex)
self.all_task_done = threading.Condition(self.mutex)
self.unfinished = 0
一把锁三个条件(self.mutex,(self.not_full, self.not_empty, self.all_task_done)),最大长度与计数君(self.maxsize,self.unfinished)
def task_done(self):
with self.all_task_done:
unfinish = self.unfinished - 1
if unfinish <= 0:
if unfinish < 0:
raise ValueError("The number of calls to task_done() is greater than the number of queue elements")
self.all_task_done.notify_all()
self.unfinished = unfinish
每一次put完都会调用一次task_done,而且调用的次数不能比队列的元素多。计数君对应的方法,unfinished<0时的意思是调用task_done的次数比列表的元素多,这种情况就会抛出异常。
def join(self):
with self.all_task_done:
while self.unfinished:
self.all_task_done.wait()
阻塞方法,是一个十分重要的方法,但它的实现也不难,只要没有完成任务就一直wait(),就是当计数君unfinished > 0 时就一直wait()知道unfinished=0跳出循环。
def put(self, item, block=True, timeout=None):
with self.not_full:
if self.maxsize > 0:
if not block:
if self._size() >= self.maxsize:
raise Exception("The BlockQueue is full")
elif timeout is not None:
self.not_full.wait()
elif timeout < 0:
raise Exception("The timeout period cannot be negative")
else:
endtime = time.time() + timeout
while self._size() >= self.maxsize:
remaining = endtime - time.time()
if remaining <= 0.0:
raise Exception("The BlockQueue is full")
self.not_full.wait(remaining)
self.queue.append(item)
self.unfinished += 1
self.not_empty.notify()
block=True一直阻塞直到有一个空闲的插槽可用,n秒内阻塞,如果在那个时间没有空闲的插槽,则会引发完全的异常。Block=False如果一个空闲的槽立即可用,则在队列上放置一个条目,否则就会引发完全的异常(在这种情况下,“超时”将被忽略)。有空位,添加到队列没结束的任务+1,他最后要唤醒not_empty.notify(),因为一开始是要在没满的情况下加锁,满了就等待not_full.wait,当put完以后就有东西了,每当一个item被添加到队列时,通知not_empty等待获取的线程会被通知。
def get(self, block=True, timeout=None):
with self.not_empty:
if not block:
if self._size() <= 0:
raise Exception("The queue is empty and you can't call get ()")
elif timeout is None:
while not self._size():
self.not_empty.wait()
elif timeout < 0:
raise ValueError("The timeout cannot be an integer")
else:
endtime = time.time() + timeout
remaining = endtime - time.time()
if remaining <= 0.0:
raise Exception("The BlockQueue is empty")
self.not_empty.wait(remaining)
item = self._get()
self.not_full.notify()
return item
如果可选的args“block”为True,并且timeout是None,则在必要时阻塞,直到有一个项目可用为止。如果“超时”是一个非负数,它会阻塞n秒,如果在那个时间内没有可get()的项,则会抛出空异常。否则'block'是False,如果立即可用,否则就会抛出空异常,在这种情况下会忽略超时。同理要唤醒not_full.notify