使用队列进行任务控制


1 FIFOLIFO队列

FIFO(First In First Out)与LIFO(Last In First Out)分别是两种队列形式,在FIFO中,满足先入先出的队列方式,而LIFO则是后入先出的队列形式,利用这两种方式可以实现不同的队列功能。

  1. from random import randint
  2. from time import sleep, ctime
  3. from queue import Queue, LifoQueue
  4. from threading import Thread
  5.  
  6. COUNT = 0
  7.  
  8. class MyThread(Thread):
  9. """
  10. Bulid up a Module to make this subclass more general
  11. And get return value by add a function named 'getResult()'
  12. """
  13. def __init__(self, func, args, name=''):
  14. Thread.__init__(self)
  15. self.name = name
  16. self.func = func
  17. self.args = args
  18.  
  19. def getResult(self):
  20. return self.res
  21.  
  22. def run(self):
  23. print('Starting', self.name, 'at:', ctime())
  24. # Call function here and calculate the running time
  25. self.res = self.func(*self.args)
  26. print(self.name, 'finished at:', ctime())
  27.  
  28. class MyQueue():
  29. def __init__(self):
  30. self.funcs = [self.writer, self.reader]
  31. self.nfuncs = range(len(self.funcs))
  32.  
  33. def writeQ(self, queue):
  34. global COUNT
  35. print('Producing object OBJ_%d for Q...' % COUNT, end=' ')
  36. queue.put('OBJ_%d' % COUNT, True)
  37. print('size now:', queue.qsize())
  38. COUNT += 1
  39.  
  40. def readQ(self, queue):
  41. # If queue is empty, block here until queue available
  42. val = queue.get(True)
  43. print('Consumed object %s from Q... size now:' % val, queue.qsize())
  44.  
  45. def writer(self, queue, loops):
  46. for i in range(loops):
  47. self.writeQ(queue)
  48. sleep(randint(1, 3))
  49.  
  50. def reader(self, queue, loops):
  51. for i in range(loops):
  52. self.readQ(queue)
  53. sleep(randint(2, 5))
  54.  
  55. def main(self):
  56. nloops = randint(2, 5)
  57. fifoQ = Queue(32)
  58. lifoQ = LifoQueue(32)
  59.  
  60. # First In First Out mode for Queue
  61. print('-----Start FIFO Queue-----')
  62. threads = []
  63. for i in self.nfuncs:
  64. threads.append(MyThread(self.funcs[i], (fifoQ, nloops), self.funcs[i].__name__))
  65. for t in threads:
  66. t.start()
  67. for t in threads:
  68. t.join()
  69. # Last In First Out mode for LifoQueue
  70. print('-----Start LIFO Queue-----')
  71. threads = []
  72. for i in self.nfuncs:
  73. threads.append(MyThread(self.funcs[i], (lifoQ, nloops), self.funcs[i].__name__))
  74. for t in threads:
  75. t.start()
  76. for t in threads:
  77. t.join()
  78.  
  79. print('All DONE')
  80.  
  81. if __name__ == '__main__':
  82. MyQueue().main()

第 1-27 行,首先对需要的模块进行导入,并定义一个全局变量的计数器,派生一个MyThread线程类,用于调用函数及其返回值(本例中MyThread可用于接受writer和reader函数,同时将Queue的实例作为参数传给这两个函数)。

第 30-79 行,定义一个队列类,用于进行队列一系列处理,其中writeQ与readQ会分别对队列执行put和get函数,在writeQ中利用全局变量设置每个加入队列的对象的名字。而writer和reader则会利用循环多次执行writeQ和readQ函数。最后定义一个main函数,用于生成队列,同时调用FIFO以及LIFO两种队列方式。

运行得到结果

  1. -----Start FIFO Queue-----
  2. Starting writer at: Tue Aug 1 21:43:22 2017
  3. Producing object OBJ_0 for Q... size now: 1
  4. Starting reader at: Tue Aug 1 21:43:22 2017
  5. Consumed object OBJ_0 from Q... size now: 0
  6. Producing object OBJ_1 for Q... size now: 1
  7. Producing object OBJ_2 for Q... size now: 2
  8. Producing object OBJ_3 for Q... size now: 3
  9. Consumed object OBJ_1 from Q... size now: 2
  10. writer finished at: Tue Aug 1 21:43:26 2017
  11. Consumed object OBJ_2 from Q... size now: 1
  12. Consumed object OBJ_3 from Q... size now: 0
  13. reader finished at: Tue Aug 1 21:43:34 2017
  14. -----Start LIFO Queue-----
  15. Starting writer at: Tue Aug 1 21:43:34 2017
  16. Producing object OBJ_4 for Q... size now: 1
  17. Starting reader at: Tue Aug 1 21:43:34 2017
  18. Consumed object OBJ_4 from Q... size now: 0
  19. Producing object OBJ_5 for Q... size now: 1
  20. Producing object OBJ_6 for Q... size now: 2
  21. Producing object OBJ_7 for Q... size now: 3
  22. writer finished at: Tue Aug 1 21:43:38 2017
  23. Consumed object OBJ_7 from Q... size now: 2
  24. Consumed object OBJ_6 from Q... size now: 1
  25. Consumed object OBJ_5 from Q... size now: 0
  26. reader finished at: Tue Aug 1 21:43:53 2017
  27. All DONE

从输出可以看出,FIFO满足先入先出,LIFO满足后入先出的队列形式。

2 join挂起与task_done信号

在queue模块中,Queue类提供了两个用于跟踪监测任务完成的函数,join和task_done,对于join函数来说,当Queue的类实例调用了join函数挂起时,join函数会阻塞等待,一直到join之前进入队列的所有任务全部标记为task_done后才会解除阻塞。

Note: 通过查看Queue的源码可以看出,在调用put函数时,会对类变量unfinished_tasks进行数值加1,而调用get函数时并不会将unfinished_tasks进行减1,只有调用task_done函数才会导致变量减1。而调用join函数时,join函数会对这个unfinished_tasks变量进行获取,也就是说,join函数会获取到在调用之前所有被put进队列里的任务中,还没有调用过task_done函数的任务数量,无论这个任务是否已经被get出列。

下面的例子中,以Queue_FIFO_LIFO.py中的MyQueue为基类,派生出一个新类,用于测试join函数与task_done函数。

  1. from Queue_FIFO_LIFO import *
  2.  
  3. class NewQueue(MyQueue):
  4. def __init__(self):
  5. MyQueue.__init__(self)
  6.  
  7. def writer(self, queue, loops):
  8. for i in range(loops):
  9. self.writeQ(queue)
  10. sleep(randint(1, 3))
  11. print('Producing join here, waiting consumer')
  12. queue.join()
  13.  
  14. def reader(self, queue, loops):
  15. for i in range(loops):
  16. self.readQ(queue)
  17. sleep(randint(2, 5))
  18. print('OBJ_%d task done' % i)
  19. queue.task_done()
  20.  
  21. def main(self):
  22. nloops = randint(2, 5)
  23. fifoQ = Queue(32)
  24.  
  25. print('-----Start FIFO Queue-----')
  26. threads = []
  27. for i in self.nfuncs:
  28. threads.append(MyThread(self.funcs[i], (fifoQ, nloops), self.funcs[i].__name__))
  29. for t in threads:
  30. t.start()
  31. for t in threads:
  32. t.join()
  33.  
  34. print('All DONE')
  35.  
  36. if __name__ == '__main__':
  37. NewQueue().main()

上面的代码,在导入模块后,调用MyQueue的初始化函数进行初始化设置。在新类NewQueue中,对原基类的writer和reader以及main方法进行了重载,加入了join函数和task_done函数,并在main函数中只采用FIFO队列进行试验。

运行得到结果

  1. -----Start FIFO Queue-----
  2. Starting writer at: Wed Aug 2 09:06:40 2017
  3. Producing object OBJ_0 for Q... size now: 1
  4. Starting reader at: Wed Aug 2 09:06:40 2017
  5. Consumed object OBJ_0 from Q... size now: 0
  6. Producing object OBJ_1 for Q... size now: 1
  7. Producing object OBJ_2 for Q... size now: 2
  8. OBJ_0 task done
  9. Consumed object OBJ_1 from Q... size now: 1
  10. Producing object OBJ_3 for Q... size now: 2
  11. Producing object OBJ_4 for Q... size now: 3
  12. Producing join here, waiting consumer
  13. OBJ_1 task done
  14. Consumed object OBJ_2 from Q... size now: 2
  15. OBJ_2 task done
  16. Consumed object OBJ_3 from Q... size now: 1
  17. OBJ_3 task done
  18. Consumed object OBJ_4 from Q... size now: 0
  19. OBJ_4 task done
  20. reader finished at: Wed Aug 2 09:07:02 2017
  21. writer finished at: Wed Aug 2 09:07:02 2017
  22. All DONE

通过得到的结果可以看出,当新类里的writer完成了自己的Producing任务后,会由join挂起,一直等待直到reader的Consuming全部完成且标记task_done之后,才会解除挂起,此时writer和reader将会一起结束退出。

相关阅读


1. 多线程的建立

2. queue 模块

参考链接


《Python 核心编程 第3版》

Python的并发并行[2] -> 队列[1] -> 使用队列进行任务控制的更多相关文章

  1. Python的并发并行[2] -> 队列[0] -> queue 模块

    queue 模块 / queue Module 1 常量 / Constants Pass 2 函数 / Function Pass 3 类 / Class 3.1 Queue类 类实例化:queue ...

  2. Python的并发并行[0] -> 基本概念

    基本概念 / Basic Concept  快速跳转 进程 / Process 线程 / Thread 协程 / Coroutine 全局解释器锁 / Global Interpreter Lock ...

  3. Python的并发并行[1] -> 线程[1] -> 多线程的建立与使用

    多线程的建立与使用 目录 生成线程的三种方法 单线程与多线程对比 守护线程的设置 1 生成线程的三种方法 三种方式分别为: 创建一个Thread实例,传给它一个函数 创建一个Thread实例,传给它一 ...

  4. Python的并发并行[1] -> 线程[2] -> 锁与信号量

    锁与信号量 目录 添加线程锁 锁的本质 互斥锁与可重入锁 死锁的产生 锁的上下文管理 信号量与有界信号量 1 添加线程锁 由于多线程对资源的抢占顺序不同,可能会产生冲突,通过添加线程锁来对共有资源进行 ...

  5. Python的并发并行[1] -> 线程[3] -> 多线程的同步控制

    多线程的控制方式 目录 唤醒单个线程等待 唤醒多个线程等待 条件函数等待 事件触发标志 函数延迟启动 设置线程障碍 1 唤醒单个线程等待 Condition类相当于一把高级的锁,可以进行一些复杂的线程 ...

  6. Python的并发并行[3] -> 进程[0] -> subprocess 模块

    subprocess 模块 0 模块描述 / Module Description From subprocess module: """Subprocesses wit ...

  7. Python的并发并行[3] -> 进程[1] -> 多进程的基本使用

    多进程的基本使用 1 subprocess 常用函数示例 首先定义一个子进程调用的程序,用于打印一个输出语句,并获取命令行参数 import sys print('Called_Function.py ...

  8. Python的并发并行[1] -> 线程[0] -> threading 模块

    threading模块 / threading Module 1 常量 / Constants Pass 2 函数 / Function 2.1 setprofile()函数 函数调用: thread ...

  9. Python的并发并行[4] -> 并发[0] -> 利用线程池启动线程

    利用线程池启动线程 submit与map启动线程 利用两种方式分别启动线程,同时利用with上下文管理来对线程池进行控制 from concurrent.futures import ThreadPo ...

随机推荐

  1. 【Gradient Boosted Decision Tree】林轩田机器学习技术

    GBDT之前实习的时候就听说应用很广,现在终于有机会系统的了解一下. 首先对比上节课讲的Random Forest模型,引出AdaBoost-DTree(D) AdaBoost-DTree可以类比Ad ...

  2. [译]16-spring基于注解的配置元数据

    从spring2.5起spring框架开始支持java注解的配置元数据.所以除了使用xml配置文件来描述bean的装配之外,你还 可以使用基于java注解的配置元数据来完成同样的功能. spring框 ...

  3. 牛客网暑期ACM多校训练营(第一场):D-Two Graphs

    链接:D-Two Graphs 题意:给出图G1和G2,求G2的子图中和G1同构的个数. 题解:只有8个点,暴力枚举G2的点每个排列,让G1映射到G2中,求出同构个数a.同构的G2就是在G1有边的对应 ...

  4. nginx索引目录配置

    为了简单共享文件,有些人使用svn,有些人使用ftp,但是更多得人使用索引(index)功能.apache得索引功能强大,并且也是最常见得,nginx得auto_index实现得目录索引偏少,而且功能 ...

  5. CentOS7 设置开机直接进入命令行界面

    上网查询centsos设置开机直接进入命令行界面的方法都说修改/etc/inittab文件,将文件中的“ :id:5:initdefault:”改为“ :id:3:initdefault:”,即将默认 ...

  6. B-Tree索引和Hash索引的区别

    Hash 索引结构的特殊性,其检索效率非常高,索引的检索可以一次定位,不像B-Tree 索引需要从根节点到枝节点,最后才能访问到页节点这样多次的IO访问,所以 Hash 索引的查询效率要远高于 B-T ...

  7. 重写Android相机适配不同的设备,对于相机旋转角度问题解决方案

    Android开发中经常需要重写相机,由此会导致一些旋转的情况(不同的设备摄像头角度是不一样的),此处按照解决思路给出解决方案: 情形一:只需要旋转摄像头方向以及最终的照片,注意两者需要保持一致 1. ...

  8. Struts1 Spring2 iBatis2 框架的集成

    这个是属于比较老的框架了,奈何现在公司用的产品就是如此,闲来就搭一个集成框架吧 依赖jar包 antlr-.jar aspectj-.jar aspectjrt.jar aspectjweaver-. ...

  9. BZOJ 2438:杀人游戏(tarjan+概率)

    杀人游戏Description一位冷血的杀手潜入 Na-wiat,并假装成平民.警察希望能在 N 个人里面,查出谁是杀手. 警察能够对每一个人进行查证,假如查证的对象是平民,他会告诉警察,他认识的人, ...

  10. Android性能优化之避免内存泄漏的建议

    在android程序开发中,内存泄漏问题是比较常见的问题,相信有过一些android编程经历的程序猿都遇到过各种各样的内存泄漏.内存泄漏是造成应用程序OOM的主要原因之一,是编程中必须避免的问题.下面 ...