发一个可伸缩线程池大小的python线程池。已通过测试。
发一个可伸缩线程池大小的线程池。
当任务不多时候,不开那么多线程,当任务多的时候开更多线程。当长时间没任务时候,将线程数量减小到一定数量。
java的Threadpoolexcutor可以这样,py的不行,修改成具备这样特性的线程池。
"""
可自动实时调节线程数量的线程池。 """ import atexit
import queue
import sys
import threading
import time
import weakref from app.utils_ydf import LoggerMixin, nb_print, LoggerLevelSetterMixin # noinspection PyShadowingBuiltins
# print = nb_print _shutdown = False
_threads_queues = weakref.WeakKeyDictionary() def _python_exit():
global _shutdown
_shutdown = True
items = list(_threads_queues.items())
for t, q in items:
q.put(None)
for t, q in items:
t.join() atexit.register(_python_exit) class _WorkItem(LoggerMixin):
def __init__(self, fn, args, kwargs):
self.fn = fn
self.args = args
self.kwargs = kwargs def run(self):
# noinspection PyBroadException
try:
self.fn(*self.args, **self.kwargs)
except BaseException as exc:
self.logger.exception(f'函数 {self.fn.__name__} 中发生错误,错误原因是 {type(exc)} {exc} ') def __str__(self):
return f'{(self.fn.__name__, self.args, self.kwargs)}' class CustomThreadPoolExecutor(LoggerMixin, LoggerLevelSetterMixin):
def __init__(self, max_workers=None, thread_name_prefix=''):
"""
最好需要兼容官方concurren.futures.ThreadPoolExecutor 和改版的BoundedThreadPoolExecutor,入参名字和个数保持了一致。
:param max_workers:
:param thread_name_prefix:
"""
self._max_workers = max_workers or 4
self._min_workers = 5
self._thread_name_prefix = thread_name_prefix
self.work_queue = queue.Queue(max_workers)
# self._threads = set()
self._threads = weakref.WeakSet()
self._lock_compute_threads_free_count = threading.Lock()
self.threads_free_count = 0
self._shutdown = False
self._shutdown_lock = threading.Lock() def set_min_workers(self, min_workers=5):
self._min_workers = min_workers
return self def change_threads_free_count(self, change_num):
with self._lock_compute_threads_free_count:
self.threads_free_count += change_num def submit(self, func, *args, **kwargs):
with self._shutdown_lock:
if self._shutdown:
raise RuntimeError('不能添加新的任务到线程池')
self.work_queue.put(_WorkItem(func, args, kwargs))
self._adjust_thread_count() def _adjust_thread_count(self):
# if len(self._threads) < self._threads_num:
self.logger.debug((self.threads_free_count, len(self._threads), len(_threads_queues), get_current_threads_num()))
if self.threads_free_count < self._min_workers and len(self._threads) < self._max_workers:
# t = threading.Thread(target=_work,
# args=(self._work_queue,self))
t = _CustomThread(self).set_log_level(self.logger.level)
t.setDaemon(True) # 这里注意是守护线程。因为线程池里面的每个线程内部进入while 1了,这样能够随时接受任务,如果不使用守护线程,会造成了程序主线程来结束了,但程序仍然无法结束。使用守护线程既能无限获得要执行的任务,又能使代码结束。
t.start()
self._threads.add(t)
_threads_queues[t] = self.work_queue def shutdown(self, wait=True):
with self._shutdown_lock:
self._shutdown = True
self.work_queue.put(None)
if wait:
for t in self._threads:
t.join() def __enter__(self):
return self def __exit__(self, exc_type, exc_val, exc_tb):
self.shutdown(wait=True)
return False class _CustomThread(threading.Thread, LoggerMixin, LoggerLevelSetterMixin):
def __init__(self, executorx: CustomThreadPoolExecutor):
super().__init__()
self._executorx = executorx
self._run_times = 0 def _remove_thread(self, stop_resson=''):
# noinspection PyUnresolvedReferences
self.logger.debug(f'停止线程 {self._ident}, 触发条件是 {stop_resson} ')
self._executorx.change_threads_free_count(-1)
self._executorx._threads.remove(self)
_threads_queues.pop(self) # noinspection PyProtectedMember
def run(self):
# noinspection PyUnresolvedReferences
self.logger.debug(f'新启动线程 {self._ident} ')
self._executorx.change_threads_free_count(1)
while True:
try:
work_item = self._executorx.work_queue.get(block=True, timeout=60)
except queue.Empty:
# continue
# self._remove_thread()
# break
if self._executorx.threads_free_count > self._executorx._min_workers:
self._remove_thread(f'当前线程超过60秒没有任务,线程池中不在工作状态中的线程数量是 {self._executorx.threads_free_count},超过了指定的数量 {self._executorx._min_workers}')
break
else:
continue # nb_print(work_item)
if work_item is not None:
self._executorx.change_threads_free_count(-1)
work_item.run()
del work_item
self._executorx.change_threads_free_count(1)
self._run_times += 1
if self._run_times == 50:
self._remove_thread(f'运行超过了50次,销毁线程')
break
continue
if _shutdown or self._executorx._shutdown:
self._executorx.work_queue.put(None)
break # @decorators.tomorrow_threads(20)
def show_current_threads_num(sleep_time=60, process_name='', block=False):
process_name = sys.argv[0] if process_name == '' else process_name def _show_current_threads_num():
while True:
nb_print(f'{process_name} 进程 的 线程数量是 --> {threading.active_count()}')
time.sleep(sleep_time) if block:
_show_current_threads_num()
else:
t = threading.Thread(target=_show_current_threads_num, daemon=True)
t.start() def get_current_threads_num():
return threading.active_count() if __name__ == '__main__':
from app.utils_ydf import decorators, BoundedThreadPoolExecutor # @decorators.keep_circulating(1)
def f1(a):
time.sleep(0.2)
nb_print(f'{a} 。。。。。。。')
# raise Exception('抛个错误测试') # show_current_threads_num()
pool = CustomThreadPoolExecutor(200).set_log_level(10).set_min_workers()
# pool = BoundedThreadPoolExecutor(200) # 测试对比原来写的BoundedThreadPoolExecutor
show_current_threads_num(sleep_time=5)
for i in range(300):
time.sleep(0.3) # 这里的间隔时间模拟,当任务来临不密集,只需要少量线程就能搞定f1了,因为f1的消耗时间短,不需要开那么多线程,CustomThreadPoolExecutor比BoundedThreadPoolExecutor 优势之一。
pool.submit(f1, str(i)) nb_print(6666)
# pool.shutdown(wait=True)
pool.submit(f1, 'yyyy') # 下面测试阻塞主线程退出的情况。注释掉可以测主线程退出的情况。
while True:
time.sleep(10)
发一个可伸缩线程池大小的python线程池。已通过测试。的更多相关文章
- python线程池ThreadPoolExecutor(上)(38)
在前面的文章中我们已经介绍了很多关于python线程相关的知识点,比如 线程互斥锁Lock / 线程事件Event / 线程条件变量Condition 等等,而今天给大家讲解的是 线程池ThreadP ...
- Android线程管理之ThreadPoolExecutor自定义线程池
前言: 上篇主要介绍了使用线程池的好处以及ExecutorService接口,然后学习了通过Executors工厂类生成满足不同需求的简单线程池,但是有时候我们需要相对复杂的线程池的时候就需要我们自己 ...
- java多线程系类:JUC线程池:03之线程池原理(二)(转)
概要 在前面一章"Java多线程系列--"JUC线程池"02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包 ...
- python线程的GIL问题(全局解释器锁)
造成原因: python ---> 支持线程操作 --->IO的同步和互斥 --> 加锁 ----> 超级锁,给解释器加锁--->解释器同一时刻只能解释一个线程 造成的后 ...
- python 线程队列PriorityQueue(优先队列)(37)
在 线程队列Queue / 线程队列LifoQueue 文章中分别介绍了先进先出队列Queue和先进后出队列LifoQueue,而今天给大家介绍的是最后一种:优先队列PriorityQueue,对队列 ...
- python线程定时器Timer(32)
相对前面几篇python线程内容而言,本片内容相对比较简单,定时器 – 顾名思义,必然用于定时任务. 一.线程定时器Timer原理 原理比较简单,指定时间间隔后启动线程!适用场景:完成定时任务,例如: ...
- 一个简单的python线程池框架
初学python,实现了一个简单的线程池框架,线程池中除Wokers(工作线程)外,还单独创建了一个日志线程,用于日志的输出.线程间采用Queue方式进行通信. 代码如下:(不足之处,还请高手指正) ...
- 一个python线程池的源码解析
python为了方便人们编程高度封装了很多东西,比如进程里的进程池,大大方便了人们编程的效率,但是默认却没有线程池,本人前段时间整理出一个线程池,并进行了简单的解析和注释,本人水平有限,如有错误希望高 ...
- [python] 线程池
特别感谢simomo 什么是线程池? 诸如web服务器.数据库服务器.文件服务器和邮件服务器等许多服务器应用都面向处理来自某些远程来源的大量短小的任务.构建服务器应用程序的一个过于简单的模型是:每当一 ...
随机推荐
- scala actor编程之对象传递
scala 最吸引人的一点就是actor并发编程了.但是纵观scala官方文档,baidu文档,IBM文档都写的通过字符串传呀传,如果用作actor编程说明当然没有问题.但是在正式开放中,光传字符串就 ...
- 变量动态选取资源ID
1.使用Resources 类的 getIdentifier方法 Resources res=getResources(); return res.getIdentifier(type ...
- linux sort按照指定列排序
sort怎样按指定的列排序0000 27189 41925425065f 15 419254250663 7 419254250675 5 419254250691 76 419254250693 2 ...
- Centos下MySQL数据库主从双向同步配置
MYSQL支持单向.异步复制,复制过程中一个服务器充当主服务器,而一个或多个其它服务器充当从服务器.主服务器将更新写入二进制日志文件,并维护日志文件的一个索引以跟踪日志循环.当一个从服务器连接到主服务 ...
- 让input表单输入框不记录输入过信息的方法
有过表单设计经验的朋友肯定知道,当我们在浏览器中输入表单信息的时候,往往input文本输入框会记录下之前提交表单的信息,以后每次只要双击input文本输入框就会出现之前输入的文本,这样有时会觉得比较方 ...
- PAT 天梯赛 L2-012. 关于堆的判断 【Heap】【STL】
题目链接 https://www.patest.cn/contests/gplt/L2-012 思路 使用 STL 里面有关 Heap 的函数 std::make_heap将[start, end)范 ...
- [算法]Evaluate Reverse Polish Notation
Evaluate the value of an arithmetic expression in Reverse Polish Notation. Valid operators are +, -, ...
- 大话设计模式--外观模式 Facade -- C++实现实例
1. 外观模式: 为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这个子系统更加容易使用. 外观模式的使用场合: A: 设计初期阶段,应该要有意识的将不同的两个层分离. ...
- Hive- 表
在hive中表的类型:管理表和托管表(外部表). 内部表也称之为MANAGER_TABLE,默认存储在/user/hive/warehouse下,也可以通过location指定:删除表时,会删除表的数 ...
- 计算地球上两个坐标点(经度,纬度)之间距离sql函数
go --计算地球上两个坐标点(经度,纬度)之间距离sql函数 --作者:lordbaby --整理:www.aspbc.com CREATE FUNCTION [dbo].[fnGetDistanc ...