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 以后,线程就变成可运行状态,一个线程在此状态下被认为 ...
随机推荐
- Integer.valueOf
一. 深入代码 在创建数字 1 的对象时, 大多数人会使用 new Integer(1), 而使用 Integer.valueOf(1) 可以使用系统缓存,既减少可能的内存占用,也省去了频繁创建对 ...
- iOS: Assertion failure on picker view
Q:I'm getting an assertion failure while scrolling a picker view w/ zero data(zero rows). While scro ...
- 创建并调用 DLL(1)
//通过 DLL Wizard 建立: library TestDLL; uses SysUtils, Classes, Dialogs; {$R *.res} //建立过程 proced ...
- Android SDK的安装教程
Android4.1虽说已经发布了好些天,但由于的我手机比较坑,系统依旧保持在2.3.4.0的都是可望不可即的了,就别说4.1.由于资金的问题,没法换手机,只能另想方法,通过在笔记本上装andro ...
- Unity获取指定资源目录下的所有文件
使用前需要引入System.IO;这个命名空间 public void GetFiles() { //路径 //string path = string.Format("{0}", ...
- 帝国CMS7.2新增多图同时上传插件,上传多图效率更高
原来上传多图文件,需要挨个选择文件,然后再点批量上传,比较麻烦.所以帝国CMS7.2新增了多图上传插件:为采用FLASH方式实现同时选择多个图片一起上传,提高多图上传效率. 帝国CMS多图上传插件特性 ...
- linux环境中,查询网卡的速度(带宽)
需求描述: 今天一同事要整理测试环境的主机硬件配置信息,需要提供网卡的速度的信息, 所以,就查询了下,在此记录下. 操作过程: 1.首先通过ip a命令查询主机的网口名称 [root@redhat6 ...
- swift swift学习笔记--函数和闭包
使用 func来声明一个函数.通过在名字之后在圆括号内添加一系列参数来调用这个方法.使用 ->来分隔形式参数名字类型和函数返回的类型 func greet(person: String, day ...
- Android移动网络如何抓取数据包
1)下载tcpdump工具 tcpdump(dump the traffic on a network)是Linux中强大的网络数据采集分析工具之一,可以将网络中传送的数据包头完全截获下来提供分析.它 ...
- Python 爬虫知识点 - 淘宝商品检索结果抓包分析(续一)
通过前一节得出地址可能的构建规律,如下: https://s.taobao.com/search?data-key=s&data-value=44&ajax=true&_ksT ...