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 以后,线程就变成可运行状态,一个线程在此状态下被认为 ...
随机推荐
- 基于Poco的UTF8、UTF16、GBK、Hex之间的转换
/******Encoding.h*******/ #include "Poco/UnicodeConverter.h" #include "Poco/Exception ...
- 解决ERROR_INVALID_USER_BUFFER
我用CSocket编写了一个HTTP下载程序,接收数据大概如下: //...... //use CSocket class //send request... //...... ); ) { Zero ...
- zabbix-agent 自动注册
1. 概述 上一篇内容<zabbix自动发现配置>,大概内容是zabbix server去扫描一个网段,把在线的主机添加到Host列表中.我们本篇内容与上篇相反,这次是Active age ...
- 详解MathType中如何插入特殊符号
在论文写作中,经常会用到一些特殊符号,MathType公式编辑器支持插入特殊符号,并且数量繁多,可以满足用户的需求.本教程将详解MathType如何插入特殊符号. MathType中插入特殊符号的操作 ...
- 记录下Lambda常用的表现形式
纯粹记录下Lambda的表现形式: (x, y) => x * y;//多参数,隐式类型=>表达式 x => x * ;//单参数,隐式类型=>表达式 x => { ; ...
- 超全面的JavaWeb笔记day11<JSP&Session&Cookie&HttpSession>
1.JSP 2.回话跟踪技术 3.Cookie 4.HttpSession JSP入门 1 JSP概述 1.1 什么是JSP JSP(Java Server Pages)是JavaWeb服务器端的动态 ...
- tableView删除功能小记
由于项目需要,做一个UITableView来实现删除功能. 效果如图: 功能思路其实不难: 交代一下,我自己要实现的效果: 1.TableView是分组的. 2.点击删除按钮后,某行被删除. 写完 ...
- PyQt4开关按钮ToggleButton
PyQt4没有开关按钮部件.但是我们可以使用在特殊状态下的QPushButton部件来创建开关按钮.而所谓的开关按钮就是一个具有按下和未按下两种状态的普通赶牛.用户可以通过单击按钮来切换其开或者关的状 ...
- Thinkphp3.2 PHPexcel 导出
1 下载phpexecl 放入到tp里边. 路径如下:项目根目录\ThinkPHP\Library\Org\Util 2 PHP 代码部分 封装一个方法 private function getE ...
- Intellij debug模式使用
1.这里以一个web工程为例,点击图中按钮开始运行web工程. 2.设置断点 3.使用postman发送http请求 4.请求发送之后会自动跳到断点处,并且在断点之前会有数据结果显示 5.按F8 在 ...