python实现阻塞队列

目录

一、初始化函数:

二、 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

二、 task_done(self):

每一次put完都会调用一次task_done,而且调用的次数不能比队列的元素多。计数君对应的方法,unfinished<0时的意思是调用task_done的次数比列表的元素多,这种情况就会抛出异常。

    def join(self):
        with self.all_task_done:
            while self.unfinished:
                self.all_task_done.wait()

三、join(self):

阻塞方法,是一个十分重要的方法,但它的实现也不难,只要没有完成任务就一直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()

四、put(self, item, block=True, timeout=None):

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

你可能感兴趣的:(python)