使用队列进行任务控制


1 FIFOLIFO队列

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

 from random import randint
from time import sleep, ctime
from queue import Queue, LifoQueue
from threading import Thread COUNT = 0 class MyThread(Thread):
"""
Bulid up a Module to make this subclass more general
And get return value by add a function named 'getResult()'
"""
def __init__(self, func, args, name=''):
Thread.__init__(self)
self.name = name
self.func = func
self.args = args def getResult(self):
return self.res def run(self):
print('Starting', self.name, 'at:', ctime())
# Call function here and calculate the running time
self.res = self.func(*self.args)
print(self.name, 'finished at:', ctime()) class MyQueue():
def __init__(self):
self.funcs = [self.writer, self.reader]
self.nfuncs = range(len(self.funcs)) def writeQ(self, queue):
global COUNT
print('Producing object OBJ_%d for Q...' % COUNT, end=' ')
queue.put('OBJ_%d' % COUNT, True)
print('size now:', queue.qsize())
COUNT += 1 def readQ(self, queue):
# If queue is empty, block here until queue available
val = queue.get(True)
print('Consumed object %s from Q... size now:' % val, queue.qsize()) def writer(self, queue, loops):
for i in range(loops):
self.writeQ(queue)
sleep(randint(1, 3)) def reader(self, queue, loops):
for i in range(loops):
self.readQ(queue)
sleep(randint(2, 5)) def main(self):
nloops = randint(2, 5)
fifoQ = Queue(32)
lifoQ = LifoQueue(32) # First In First Out mode for Queue
print('-----Start FIFO Queue-----')
threads = []
for i in self.nfuncs:
threads.append(MyThread(self.funcs[i], (fifoQ, nloops), self.funcs[i].__name__))
for t in threads:
t.start()
for t in threads:
t.join()
# Last In First Out mode for LifoQueue
print('-----Start LIFO Queue-----')
threads = []
for i in self.nfuncs:
threads.append(MyThread(self.funcs[i], (lifoQ, nloops), self.funcs[i].__name__))
for t in threads:
t.start()
for t in threads:
t.join() print('All DONE') if __name__ == '__main__':
MyQueue().main()

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

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

运行得到结果

-----Start FIFO Queue-----
Starting writer at: Tue Aug 1 21:43:22 2017
Producing object OBJ_0 for Q... size now: 1
Starting reader at: Tue Aug 1 21:43:22 2017
Consumed object OBJ_0 from Q... size now: 0
Producing object OBJ_1 for Q... size now: 1
Producing object OBJ_2 for Q... size now: 2
Producing object OBJ_3 for Q... size now: 3
Consumed object OBJ_1 from Q... size now: 2
writer finished at: Tue Aug 1 21:43:26 2017
Consumed object OBJ_2 from Q... size now: 1
Consumed object OBJ_3 from Q... size now: 0
reader finished at: Tue Aug 1 21:43:34 2017
-----Start LIFO Queue-----
Starting writer at: Tue Aug 1 21:43:34 2017
Producing object OBJ_4 for Q... size now: 1
Starting reader at: Tue Aug 1 21:43:34 2017
Consumed object OBJ_4 from Q... size now: 0
Producing object OBJ_5 for Q... size now: 1
Producing object OBJ_6 for Q... size now: 2
Producing object OBJ_7 for Q... size now: 3
writer finished at: Tue Aug 1 21:43:38 2017
Consumed object OBJ_7 from Q... size now: 2
Consumed object OBJ_6 from Q... size now: 1
Consumed object OBJ_5 from Q... size now: 0
reader finished at: Tue Aug 1 21:43:53 2017
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函数。

 from Queue_FIFO_LIFO import *

 class NewQueue(MyQueue):
def __init__(self):
MyQueue.__init__(self) def writer(self, queue, loops):
for i in range(loops):
self.writeQ(queue)
sleep(randint(1, 3))
print('Producing join here, waiting consumer')
queue.join() def reader(self, queue, loops):
for i in range(loops):
self.readQ(queue)
sleep(randint(2, 5))
print('OBJ_%d task done' % i)
queue.task_done() def main(self):
nloops = randint(2, 5)
fifoQ = Queue(32) print('-----Start FIFO Queue-----')
threads = []
for i in self.nfuncs:
threads.append(MyThread(self.funcs[i], (fifoQ, nloops), self.funcs[i].__name__))
for t in threads:
t.start()
for t in threads:
t.join() print('All DONE') if __name__ == '__main__':
NewQueue().main()

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

运行得到结果

-----Start FIFO Queue-----
Starting writer at: Wed Aug 2 09:06:40 2017
Producing object OBJ_0 for Q... size now: 1
Starting reader at: Wed Aug 2 09:06:40 2017
Consumed object OBJ_0 from Q... size now: 0
Producing object OBJ_1 for Q... size now: 1
Producing object OBJ_2 for Q... size now: 2
OBJ_0 task done
Consumed object OBJ_1 from Q... size now: 1
Producing object OBJ_3 for Q... size now: 2
Producing object OBJ_4 for Q... size now: 3
Producing join here, waiting consumer
OBJ_1 task done
Consumed object OBJ_2 from Q... size now: 2
OBJ_2 task done
Consumed object OBJ_3 from Q... size now: 1
OBJ_3 task done
Consumed object OBJ_4 from Q... size now: 0
OBJ_4 task done
reader finished at: Wed Aug 2 09:07:02 2017
writer finished at: Wed Aug 2 09:07:02 2017
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. ICG-智能代码生成器.(权限控制.融入平台).(表单引擎).(最低兼容IE8)

    请下拉滚动条... 代码生成器.附带客户端代码 个人平台:www.10086bank.com 界面: 1--首先是server制作界面(BS结构).直接上图:   2--点击提交生成一下文件: 各个代 ...

  2. TIDB介绍

    TiDB 是什么? TiDB 是一个分布式 NewSQL 数据库.它支持水平弹性扩展.ACID 事务.标准 SQL.MySQL 语法和 MySQL 协议,具有数据强一致的高可用特性,是一个不仅适合 O ...

  3. Git——1.简介

    关于版本控制 Git基础 安装Git 初始运行Git前的配置 获取帮助 关于版本控制 版本控制(VCS)是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统. 本地版本控制系统 大多都 ...

  4. java程序员笑不死的经历ส้้้้้้้้้

    ส้้้้้้้้้้ส้้้้้้้้้้ส้้้้้้้้้ 1.程序猿最烦两件事,第一件事是别人要求他给自己的代码写文档,第二件呢?是别人的程序没有留下文档. 2.宪法顶个球!中国的法律都是.t ...

  5. [OpenCV] Ptr类模板

    1.C++泛型句柄类 我们知道在包含指针成员的类中,需要特别注意类的复制控制,因为复制指针时只复制指针中的地址,而不会复制指针指向的对象.这将导致当两个指针同时指向同一对象时,很可能一个指针删除了一对 ...

  6. Scrapy 学习笔记(一)数据提取

    Scrapy 中常用的数据提取方式有三种:Css 选择器.XPath.正则表达式. Css 选择器 Web 中的 Css 选择器,本来是用于实现在特定 DOM 元素上应用花括号内的样式这样一个功能的. ...

  7. MYSQL 简单的建库操作代码

    一.查询所有数据库 代码:show databases; 成功后如下图: 二.建立一个数据库 代码:create database test3: 成功后如下图: 三.连接数据库 代码:use test ...

  8. background-size属性的几个实用的值

    先来看w3c的background-size的几个值: background-size:cover;    把背景图像扩展至足够大,以使背景图像完全覆盖背景区域.背景图像的某些部分也许无法显示在背景定 ...

  9. 《R语言实战》读书笔记--为什么要学

    本人最近在某咨询公司实习,涉及到了一些数据分析的工作,用的是R语言来处理数据.但是在应用的过程中,发现用R很不熟练,所以再打算学一遍R.曾经花一个月的时间看过一遍<R语言编程艺术>,还用R ...

  10. poj 3648 Wedding 2-SAT问题入门题目

    Description Up to thirty couples will attend a wedding feast, at which they will be seated on either ...