python基础-第九篇-9.3线程池
简单版

import queue
import threading class ThreadPool(object): def __init__(self, max_num=20):
self.queue = queue.Queue(max_num)
for i in range(max_num):
self.queue.put(threading.Thread) def get_thread(self):
return self.queue.get() def add_thread(self):
self.queue.put(threading.Thread) #实例化线程池对象
pool = ThreadPool(10)
#执行构造方法--实例一个max_num=10的队列,并往队列上添加10个线程类 def func(arg, p):
print(arg)
import time
time.sleep(2)
#在线程执行完前往队列里再加上一个线程类
p.add_thread() #生成30个任务
for i in range(30):
#每生成一个任务就从队列里取出一个线程类
thread = pool.get_thread()
#并实例化线程对象来执行任务
t = thread(target=func, args=(i, pool))
#线程启动后,执行func函数
t.start()
我们可以看到,简单版的线程池就是简单的,逻辑就是每次执行一个任务就从队列取一个线程类创建一个线程,所以就有了--多少个任务,多少个线程,那和进程池相比较下,你会从中发现哪些不足呢?
- 第一,首先问你,执行完的线程去哪呢?--被程序回收销毁了! 那么执行完这些任务有必要一对一的创建线程吗??--如果任务执行的快,一个线程有时候可以做多个任务
- 第二,上面只规定了队列的长度,并没有规定线程池里的线程数量??
- 第三,进程池有回调函数这么一说法,上面没有?
- 第四,进程池里提供了close和terminate方法...
绝版

好!我们来看看代码,是怎样解决上面不足的:
import queue
import threading
import contextlib
import time #放入队列里,做为线程停止运行的信号
StopEvent = object() class ThreadPool(object): def __init__(self, max_num, max_task_num = None):
#对max_task_num进行判断
if max_task_num:
#如果有值传入,在创建队列时,就按传入值限定队列长度
self.q = queue.Queue(max_task_num)
else:
#否则,就默认为队列长度为无限长
self.q = queue.Queue()
#最大线程数
self.max_num = max_num
#close方法的状态码
self.cancel = False
#terminate方法的状态码
self.terminal = False
#生成线程列表
self.generate_list = []
#空闲线程列表
self.free_list = [] def run(self, func, args, callback=None):
"""
线程池执行一个任务
:param func: 任务函数
:param args: 任务函数所需参数
:param callback: 任务执行失败或成功后执行的回调函数,回调函数有两个参数1、任务函数执行状态;2、任务函数返回值(默认为None,即:不执行回调函数)
:return: 如果线程池已经终止,则返回True否则None
""" #如果执行close方法,这里就会执行return--中止函数,等同不再创建线程
if self.cancel:
return
#如果空闲线程列表里为空并且已生成的线程数没有超过规定的最大线程数
if len(self.free_list) == 0 and len(self.generate_list) < self.max_num:
#那么就调用generate_thread方法创建线程
self.generate_thread()
#打包任务
w = (func, args, callback,)
#把打包的任务放入到队列里
self.q.put(w) def generate_thread(self): #创建了一个线程
t = threading.Thread(target=self.call)
#启动线程,执行call方法
t.start() def call(self):
"""
循环去获取任务函数并执行任务函数
""" #获取当前线程变量
current_thread = threading.currentThread
#把当前线程添加到生成线程列表里
self.generate_list.append(current_thread) #到队列里去拿任务
event = self.q.get()
#对取到的任务进行判断
while event != StopEvent:
#如果任务不等于停止信号,进入循环 #解任务包
func, arguments, callback = event
#尝试执行任务包里的函数
try:
#执行成功,拿到result执行结果
result = func(*arguments)
#并给执行状态赋值True
success = True
except Exception as e:
#执行失败,赋值执行状态False
success = False
#同时赋值执行结果为None
result = None if callback is not None:
#如果callback不是默认None,就尝试下列操作
try:
#把执行状态和执行结果传给回调函数,执行
callback(success, result)
except Exception as e:
pass #with上下文管理--等同把free_list,current_thread传给worker_state方法,并执行
with self.worker_state(self.free_list, current_thread):
#判断self.terminal的状态
if self.terminal:
#如果状态为True,就把任务变量设置为停止信号,等同于中止当前线程
event = StopEvent
else:
#否则,就去队列里取任务
event = self.q.get()
else:
#如果是停止信号,就把当前线程从生成线程列表中移除
self.generate_list.remove(current_thread) def close(self):
"""
执行完所有的任务后,所有线程停止
""" #当执行close方法时,就把cancel状态设置为True
self.cancel = True
full_size = len(self.generate_list)
while full_size:
#往队列里加停止信号,直到生成线程列表长度为0
self.q.put(StopEvent)
full_size -= 1 def terminate(self):
"""
无论是否还有任务,终止线程
""" #当执行terminate方法时,把terminal状态设置为True
self.terminal = True
#因为突然中止,难免队列里还有任务,所以先清空一下队列
self.q.empty()
while self.generate_list:
#往队列里加停止信号,值到生成线程列表为空时
self.q.put(StopEvent) @contextlib.contextmanager
def worker_state(self, state_list, worker_thread):
"""
用于记录线程中正在等待的线程数
""" #把执行完任务的当前线程添加到空闲线程列表里
state_list.append(worker_thread)
try:
#暂时跳出
yield
finally:
#把当前线程从空闲线程列表里移除
state_list.remove(worker_thread) # How to use pool = ThreadPool(5) def callback(status, result):
# status, execute action status
# result, execute action return value
pass def action(i):
print(i) for i in range(30):
ret = pool.run(action, (i,), callback) time.sleep(5)
print(len(pool.generate_list), len(pool.free_list))
print(len(pool.generate_list), len(pool.free_list))
# pool.close()
# pool.terminate()

欢迎大家对我的博客内容提出质疑和提问!谢谢
笔者:拍省先生
python基础-第九篇-9.3线程池的更多相关文章
- python基础-第九篇-9.2线程与多线程
单线程 import time beginTime = time.time() for a in range(10): print(a) time.sleep(1) shijian = time.ti ...
- python基础-第九篇-9.1初了解Python线程、进程、协程
了解相关概念之前,我们先来看一张图 进程: 优点:同时利用多个cpu,能够同时进行多个操作 缺点:耗费资源(重新开辟内存空间) 线程: 优点:共享内存,IO操作时候,创造并发操作 缺点:抢占资源 通过 ...
- Python 基础学习篇
注:技术尚浅,时间匆忙,如有错误或者不当之处值得商榷的,请留言,吾必思而改之. 第一篇 :Python基础- 安装/变量/输入/及循环语句使用 第二篇: Python基础- 常用数据类型 第三篇: ...
- python第十一天-----补:线程池
低版本: #!/usr/bin/env python import threading import time import queue class TreadPool: ""&q ...
- Python并发复习4- concurrent.futures模块(线程池和进程池)
Python标准库为我们提供了threading(多线程模块)和multiprocessing(多进程模块).从Python3.2开始,标准库为我们提供了concurrent.futures模块,它提 ...
- python(13)多线程:线程池,threading
python 多进程:多进程 先上代码: pool = threadpool.ThreadPool(10) #建立线程池,控制线程数量为10 reqs = threadpool.makeRequest ...
- java并发编程JUC第九篇:CountDownLatch线程同步
在之前的文章中已经为大家介绍了java并发编程的工具:BlockingQueue接口.ArrayBlockingQueue.DelayQueue.LinkedBlockingQueue.Priorit ...
- 第九章 Java中线程池
Java中的线程池是运用场景最多的并发框架,几乎所有需求异步或并发执行任务的程序都可以使用线程池.在开发过程中,合理地使用线程池能够带来3个好处. 降低资源消耗:通过重复利用已创建的线程降低线程创建和 ...
- Java基础知识(多线程和线程池)
新建状态: 一个新产生的线程从新状态开始了它的生命周期.它保持这个状态直到程序 start 这个线程. 运行状态:当一个新状态的线程被 start 以后,线程就变成可运行状态,一个线程在此状态下被认为 ...
随机推荐
- ubuntu 访问 共享 windows文件夹
sudo mount -o username=*******,password=******** //192.168.1.105/迅雷下载 /mnt/
- MultiplyVector方法
该方法的功能是用来对方向向量v进行矩阵变换,当矩阵与v进行变换时,只是对v的方向进行转换.那么系统就会对参与变换的Matrix4x4矩阵M进行特殊处理. 例如:经过系统处理后就变为 其中n002 + ...
- jquery中判断选择器,找没找到元素用$().size()==0
jquery中判断选择器,找没找到元素用$().size()==0
- C语言中,为什么字符串可以赋值给字符指针变量
转载于:http://www.cnblogs.com/KingOfFreedom/archive/2012/12/07/2807223.html 本文是通过几篇转帖的文章整理而成的,内容稍有修改: 一 ...
- C/C++ 头文件以及库的搜索路径
关键点: 1. #include <...> 不会搜索当前目录 2. 使用 -I 参数指定的头文件路径仅次于 搜索当前路径. 3. gcc -E -v 可以输出头文件路径搜索过程 C++编 ...
- laravel 5.3 多用户认证
不知道对不对,乱来一下!!!! 1)laravel自带了一个用户认证系统,要使用的话,直接运行一下命令就可以用了 php artisan make:auth 会生成相应的控制器.路由和模版文件 2)数 ...
- linux 添加交换分区
[操作简介] 增加swap分区方法: 1.新建磁盘分区作为swap分区 2.用文件作为swap分区 (操作更简单,我更常用) 下面介绍这两种方法:(都必须用root权限,操作过程应该小心谨慎.) ...
- 运行 3ds Max 时出现的性能问题
运行 3ds Max 时性能减慢或迟缓通常是由于视频配置冲突或内存分配问题引起的.关于性能问题的一大难点在于缩小范围确定问题原因.以下是一些限制 3ds Max 操作的常见情形,以及纠正这 ...
- 电脑CPU的发展史(转)
Intel于1971年发售了自己的第一款4位微处理器,设计与ROM 4001,RAM 4002和移位寄存器4003配合工作.其中4004自身负责运算,其它部分则是使CPU正常工作的重要组成.大部分40 ...
- ubuntu下code::blocks编译运行一个简单的gtk+2.0项目
在具体的操作之前,首先需要安装一些必要的软件.ubuntu下默认安装了gcc,不过缺少必要的Header file,可以在命令行中输入下面的指令安装build-essential套件:sudo apt ...