Python 内置队列 SimpleQueue 代码解析

542 阅读2分钟

「这是我参与2022首次更文挑战的第17天,活动详情查看:2022首次更文挑战」。

正式的Python专栏第66篇,同学站住,别错过这个从0开始的文章!

之前看内置队列发现才几百行代码,没细看。 本来是想’简单‘写写文章,速读一下,可是这个队列用的其他库还挺多的,所以篇幅就更大了。

一大意容易栽坑里挖,走不出来,所以尽量克制一点,今天我们看看简单队列:SimpleQueue

在看之前,很有必要介绍一下:双端队列

因为SimpleQueue的源码里面主要用了这个队列,所以需要先铺垫解释

简单来说,双端队列 就是可以两边都插入数据的队列,两边都取出数据的队列。

哇塞,很猛的样子!(其实内部也是有很多设计,双端队列这个坑这里不深挖了,小短文是写不完的。)

from collections import deque

dq = deque([])

上面是创建双端队列的代码,它有一套对称的添加/获取元素的方法,比如append,appendleft 或者 pop ,popleft(对应的是右边加新元素,左边加新元素 和 右边取出新元素,左边取出新元素),还有很多方法,但是本文不多谈。

学委这里写了一个demo,展示双端队列左右端的元素操作:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time : 2022/2/17 11:30 下午
# @Author : LeiXueWei
# @CSDN/Juejin/Wechat: 雷学委
# @XueWeiTag: CodingDemo
# @File : dequedemo.py
# @Project : hello
from collections import deque

dq = deque([]) #Double-Ended Queue
dq.append("01") #append即可,它就是append right
dq.append("02")
dq.append("03")
print("dq:", dq)

dq.appendleft("a")
dq.appendleft("b")
dq.appendleft("c")
print("dq:", dq)

# 右边取出一个元素
v = dq.pop()
print("v:", v)
print("dq:", dq)
# 左边取出一个元素
v = dq.popleft()
print("v:", v)
print("dq:", dq)

运行效果如下:

屏幕快照 2022-02-17 下午11.49.59.png

好,简单队列隆重登场

这个之前文章写过demo了。

打好了双端队列基础之后,我们看简单队列就简单多了。

几乎都不用解析了。。。 当然也不是。

我们看到简单队列在初始化的时候,创建了一个信号量为0的变量。

好家伙,这样做,只能是调用了release方法之后,才能acquire了, 不清楚信号量的朋友可以看:学委之前写的信号量相关文章

class _PySimpleQueue:

    def __init__(self):
        self._queue = deque()
        self._count = threading.Semaphore(0)

    def put(self, item, block=True, timeout=None):
        self._queue.append(item)
        self._count.release()

    def get(self, block=True, timeout=None):
        if timeout is not None and timeout < 0:
            raise ValueError("'timeout' must be a non-negative number")
        if not self._count.acquire(block, timeout):
            raise Empty
        return self._queue.popleft()

    def put_nowait(self, item):
        return self.put(item, block=False)

    def get_nowait(self):
        return self.get(block=False)

    def empty(self):
        return len(self._queue) == 0

    def qsize(self):
        return len(self._queue)


if SimpleQueue is None:
    SimpleQueue = _PySimpleQueue

我们重点看一下put方法。

put方法,追加元素(每次放入都在队列右边加入),同时释放一个信号量(可用信号量+1)。 仅此而已。

下面是get方法解析:

默认情况下创建了简单队列,直接去get,线程会一直等待,知道有元素放入简单队列(也就是有线程调用了put方法)。

如果是get传入block=False,并且没有设置timeout,那就是get_nowait的情况,这个时候信号量acquire获取的值是False,抛出空值异常了。

当我们放了元素到简单队列(或者多个),后面加的元素都是在deque右边的,最左边的元素就是最新加入元素,所以程序直接popleft。

这样简单队列通过deque保证了元素先进先出原则

所以get_nowait, pop_nowait等等这些都不需要多解释。

而qsize,empty这些常规队列配套方法看过前面的文章都知道。

总结

简单队列内核用了双端队列,实现元素的添加,删除,再说一次‘先进先出’。而且它是线程安全的。

喜欢Python的朋友,请关注学委的 Python基础专栏 or Python入门到精通大专栏

持续学习持续开发,我是雷学委!
编程很有趣,关键是把技术搞透彻讲明白。
欢迎关注微信,点赞支持收藏!