起步

queue 模块提供适用于多线程编程的先进先出(FIFO)数据结构。因为它是线程安全的,所以多个线程很轻松地使用同一个实例。

源码分析

先从初始化的函数来看:

从这初始化函数能得到哪些信息呢?首先,队列是可以设置其容量大小的,并且具体的底层存放元素的它使用了collections.deque()双端列表的数据结构,这使得能很方便的做先进先出操作。这里还特地抽象为_init函数是为了方便其子类进行覆盖,允许子类使用其他结构来存放元素(比如优先队列使用了 list)。

然后就是线程锁self.mutex,对于底层数据结构self.queue的操作都要先获得这把锁;再往下是三个条件变量,这三个 Condition 都以self.mutex作为参数,也就是说它们共用一把锁;从这可以知道诸如with self.mutex与with self.not_empty等都是互斥的。

基于这些锁而做的一些简单的操作:

这个代码片段挺好理解的,无需分析。

作为队列,主要得完成入队与出队的操作,首先是入队:

尽管只有二十几行的代码,但这里的逻辑还是比较复杂的。它要处理超时与队列剩余空间不足的情况,具体几种情况如下:

1、如果 block 是 False,忽略timeout参数

若此时队列已满,则抛出 Full 异常;

若此时队列未满,则立即把元素保存到底层数据结构中;

2、如果 block 是 True

若 timeout 是 None 时,那么put操作可能会阻塞,直到队列中有空闲的空间(默认);

若 timeout 是非负数,则会阻塞相应时间直到队列中有剩余空间,在这个期间,如果队列中一直没有空间,抛出 Full 异常;

处理好参数逻辑后,,将元素保存到底层数据结构中,并递增unfinished_tasks,同时通知not_empty,唤醒在其中等待数据的线程。

出队操作:

get()操作是put()相反的操作,代码块也及其相似,get()是从队列中移除最先插入的元素并将其返回。

1、如果 block 是 False,忽略timeout参数

若此时队列没有元素,则抛出 Empty 异常;

若此时队列由元素,则立即把元素保存到底层数据结构中;

2、如果 block 是 True

若 timeout 是 None 时,那么get操作可能会阻塞,直到队列中有元素(默认);

若 timeout 是非负数,则会阻塞相应时间直到队列中有元素,在这个期间,如果队列中一直没有元素,则抛出 Empty 异常;

最后,通过self.queue.popleft()将最早放入队列的元素移除,并通知not_full,唤醒在其中等待数据的线程。

这里有个值得注意的地方,在put()操作中递增了self.unfinished_tasks,而get()中却没有递减,这是为什么?

这其实是为了留给用户一个消费元素的时间,get()仅仅是获取元素,并不代表消费者线程处理的该元素,用户需要调用task_done()来通知队列该任务处理完成了:

由于task_done()使用方调用的,当task_done()次数大于put()次数时会抛出异常。

task_done()操作的作用是唤醒正在阻塞的join()操作。join()方法会一直阻塞,直到队列中所有的元素都被取出,并被处理了(和线程的join方法类似)。也就是说join()方法必须配合task_done()来使用才行。

LIFO 后进先出队列

LifoQueue使用后进先出顺序,与栈结构相似:

这就是 LifoQueue 全部代码了,这正是 Queue 设计很棒的一个原因,它将底层的数据操作抽象成四个操作函数,本身来处理线程安全的问题,使得其子类只需关注底层的操作。

LifoQueue 底层数据结构改用 list 来存放,通过 self.queue.pop() 就能将 list 中最后一个元素移除,无需重置索引。

PriorityQueue 优先队列

优先队列使用了 heapq 模块的结构,也就是最小堆的结构。优先队列更为常用,队列中项目的处理顺序需要基于这些项目的特征,一个简单的例子:

使用优先队列的时候,需要定义__lt__魔术方法,来定义它们之间如何比较大小。若元素的 priority 相同,依然使用先进先出的顺序。
---------------------

最新文章

  1. View (一)LayoutInflater()方法详解
  2. 备份mysql
  3. x64、x86_64、x64三者的区别
  4. The secret code
  5. ACM1994
  6. MVC 生成PDf表格并插入图片
  7. [线段树]HDOJ5091 Beam Cannon
  8. 第一篇:GPU 编程技术的发展历程及现状
  9. linux解压命令
  10. Oracle 同步表权限分配(同义词)
  11. C++中Map常见用法以及按照value排序
  12. ELK 经典用法—企业自定义日志手机切割和mysql模块
  13. SQL server 2008 安装提示:属性不匹配
  14. IO 和 NIO 的思考
  15. Java开发笔记(八)五种算术运算符
  16. docker相关操作文章整理
  17. 洛谷P1516 青蛙的约会(扩展欧几里德)
  18. HTTP SIP 认证
  19. hive入门
  20. Angular4 自制华容道拼图(可以升级难度、关卡、更换图片)

热门文章

  1. Lightoj 1017 - Brush (III)
  2. Difference between HttpContext.Request and Request
  3. YTU 2895: H--唱歌的鸟儿
  4. 工作笔记——sqlserver引号的运用
  5. JS事件流与DOM事件处理程序
  6. excel+requests管理测试用例接口自动化框架
  7. [转]python的startswith()方法
  8. context:property-placeholder作用
  9. Pessimistic Offline Lock悲观离线锁
  10. WinHTTrack