python3中线程池
1.在使用多线程处理任务时也不是线程越多越好,由于在切换线程的时候,需要切换上下文环境,依然会造成cpu的大量开销。为解决这个问题,线程池的概念被提出来了。预先创建好一个较为优化的数量的线程,让过来的任务立刻能够使用,就形成了线程池。在python中,没有内置的较好的线程池模块,需要自己实现或使用第三方模块。下面是一个简单的线程池:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | import threading,time,os,queue class ThreadPool( object ): def __init__( self ,maxsize): self .maxsize = maxsize self ._q = queue.Queue( self .maxsize) for i in range ( self .maxsize): self ._q.put(threading.Thread) def getThread( self ): return self ._q.get() def addThread( self ): self ._q.put(threading.Thread) def fun(num,p): print ( 'this is thread [%s]' % num) time.sleep( 1 ) p.addThread() if __name__ = = '__main__' : pool = ThreadPool( 2 ) for i in range ( 103 ): t = pool.getThread() a = t(target = fun,args = (i,pool)) a.start() |
上面的例子是把线程类当做元素添加到队列内。实现方法比较糙,每个线程使用后就被抛弃,一开始就将线程开到满,因此性能较差。下面是一个相对好一点的例子,在这个例子中,队列里存放的不再是线程对象,而是任务对象,线程池也不是一开始就直接开辟所有线程,而是根据需要,逐步建立,直至池满。通过详细的代码注释,应该会有个清晰的理解。
2.高级版线程池
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 | """ 一个基于thread和queue的线程池,以任务为队列元素,动态创建线程,重复利用线程, 通过close和terminate方法关闭线程池。 """ import queue import threading import contextlib import time # 创建空对象,用于停止线程 StopEvent = object () def callback(status, result): """ 根据需要进行的回调函数,默认不执行。 :param status: action函数的执行状态 :param result: action函数的返回值 :return: """ pass def action(thread_name,arg): """ 真实的任务定义在这个函数里 :param thread_name: 执行该方法的线程名 :param arg: 该函数需要的参数 :return: """ # 模拟该函数执行了0.1秒 time.sleep( 0.1 ) print ( "第%s个任务调用了线程 %s,并打印了这条信息!" % (arg + 1 , thread_name)) class ThreadPool: def __init__( self , max_num, max_task_num = None ): """ 初始化线程池 :param max_num: 线程池最大线程数量 :param max_task_num: 任务队列长度 """ # 如果提供了最大任务数的参数,则将队列的最大元素个数设置为这个值。 if max_task_num: self .q = queue.Queue(max_task_num) # 默认队列可接受无限多个的任务 else : self .q = queue.Queue() # 设置线程池最多可实例化的线程数 self .max_num = max_num # 任务取消标识 self .cancel = False # 任务中断标识 self .terminal = False # 已实例化的线程列表 self .generate_list = [] # 处于空闲状态的线程列表 self .free_list = [] def put( self , func, args, callback = None ): """ 往任务队列里放入一个任务 :param func: 任务函数 :param args: 任务函数所需参数 :param callback: 任务执行失败或成功后执行的回调函数,回调函数有两个参数 1、任务函数执行状态;2、任务函数返回值(默认为None,即:不执行回调函数) :return: 如果线程池已经终止,则返回True否则None """ # 先判断标识,看看任务是否取消了 if self .cancel: return # 如果没有空闲的线程,并且已创建的线程的数量小于预定义的最大线程数,则创建新线程。 if len ( self .free_list) = = 0 and len ( self .generate_list) < self .max_num: self .generate_thread() # 构造任务参数元组,分别是调用的函数,该函数的参数,回调函数。 w = (func, args, callback,) # 将任务放入队列 self .q.put(w) def generate_thread( self ): """ 创建一个线程 """ # 每个线程都执行call方法 t = threading.Thread(target = self .call) t.start() def call( self ): """ 循环去获取任务函数并执行任务函数。在正常情况下,每个线程都保存生存状态, 直到获取线程终止的flag。 """ # 获取当前线程的名字 current_thread = threading.currentThread().getName() # 将当前线程的名字加入已实例化的线程列表中 self .generate_list.append(current_thread) # 从任务队列中获取一个任务 event = self .q.get() # 让获取的任务不是终止线程的标识对象时 while event ! = StopEvent: # 解析任务中封装的三个参数 func, arguments, callback = event # 抓取异常,防止线程因为异常退出 try : # 正常执行任务函数 result = func(current_thread, * arguments) success = True except Exception as e: # 当任务执行过程中弹出异常 result = None success = False # 如果有指定的回调函数 if callback is not None : # 执行回调函数,并抓取异常 try : callback(success, result) except Exception as e: pass # 当某个线程正常执行完一个任务时,先执行worker_state方法 with self .worker_state( self .free_list, current_thread): # 如果强制关闭线程的flag开启,则传入一个StopEvent元素 if self .terminal: event = StopEvent # 否则获取一个正常的任务,并回调worker_state方法的yield语句 else : # 从这里开始又是一个正常的任务循环 event = self .q.get() else : # 一旦发现任务是个终止线程的标识元素,将线程从已创建线程列表中删除 self .generate_list.remove(current_thread) def close( self ): """ 执行完所有的任务后,让所有线程都停止的方法 """ # 设置flag self .cancel = True # 计算已创建线程列表中线程的个数,然后往任务队列里推送相同数量的终止线程的标识元素 full_size = len ( self .generate_list) while full_size: self .q.put(StopEvent) full_size - = 1 def terminate( self ): """ 在任务执行过程中,终止线程,提前退出。 """ self .terminal = True # 强制性的停止线程 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) # 调用方式 if __name__ = = '__main__' : # 创建一个最多包含5个线程的线程池 pool = ThreadPool( 5 ) # 创建100个任务,让线程池进行处理 for i in range ( 100 ): pool.put(action, (i,), callback) # 等待一定时间,让线程执行任务 time.sleep( 3 ) print ( "-" * 50 ) print ( "\033[32;0m任务停止之前线程池中有%s个线程,空闲的线程有%s个!\033[0m" % ( len (pool.generate_list), len (pool.free_list))) # 正常关闭线程池 pool.close() print ( "任务执行完毕,正常退出!" ) # 强制关闭线程池 # pool.terminate() # print("强制停止任务!") |
3.利用简单线程池和paramiko实现对远程服务器的访问获取到相关信息:(自己写的例子,比较low)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | import paramiko,threading import queue class ThreadPool( object ): def __init__( self ,maxsize): self .maxsize = maxsize self ._q = queue.Queue( self .maxsize) for i in range ( self .maxsize): self ._q.put(threading.Thread) def getThread( self ): return self ._q.get() def addThread( self ): self ._q.put(threading.Thread) def ssh_fun(ip,user,password,pool): try : ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect(ip, 22 , user, password) stdin, stdout, stderr = ssh.exec_command( 'hostname' ) info = stdout.read().decode().strip() print ( 'IP:%s hostname:%s' % (ip,info)) ssh.close() except Exception: print ( 'sorry I can`t connect this server [%s]' % ip) pool.addThread() if __name__ = = '__main__' : t_list = [] pool = ThreadPool( 2 ) with open ( 'aaa' , 'r+' ,encoding = 'utf-8' ) as f: for line in f: split = line.split() ip,user,password = split[ 0 ],split[ 1 ],split[ 2 ] th = pool.getThread() t = th(target = ssh_fun,args = (ip,user,password,pool)) t.start() t_list.append(t) for i in t_list: i.join() |
在这里我为了测试线程池中只有两个线程,并且我这个是读取aaa文件的,这个文件中包含用户名和密码等相关信息,样子如下(其实可以把这些放进数据库中,使用python从数据库中进行读取):
1 2 3 4 | 192.168.32.167 root 111111 192.168.32.110 root 111111 192.168.32.120 root 111111 192.168.32.150 root 111111 |
而最后执行的效果如下:
IP:192.168.32.167 hostname:ns.root
sorry I can`t connect this server [192.168.32.110]
IP:192.168.32.150 hostname:localhost.localdomain
sorry I can`t connect this server [192.168.32.120]
4.改进版结合了mysql向数据库中插入数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | import paramiko,threading import queue import pymysql class ThreadPool( object ): def __init__( self ,maxsize): self .maxsize = maxsize self ._q = queue.Queue( self .maxsize) for i in range ( self .maxsize): self ._q.put(threading.Thread) def getThread( self ): return self ._q.get() def addThread( self ): self ._q.put(threading.Thread) def ssh_fun(ip,user,password,pool,db): cursor = db.cursor() try : ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect(ip, 22 , user, password) stdin, stdout, stderr = ssh.exec_command( 'hostname' ) info = stdout.read().decode().strip() print ( 'IP:%s hostname:%s' % (ip,info)) try : cursor.execute( 'insert into server_status(ip,password,hostname) values ("%s","%s","%s")' % (ip,password,info)) db.commit() except : db.rollback() ssh.close() except Exception: print ( 'sorry I can`t connect this server [%s]' % ip) pool.addThread() if __name__ = = '__main__' : t_list = [] pool = ThreadPool( 3 ) db = pymysql.connect( '192.168.32.188' , 'hjc' , '111111' , 'hjc' ) with open ( 'aaa' , 'r+' ,encoding = 'utf-8' ) as f: for line in f: split = line.split() ip,user,password = split[ 0 ],split[ 1 ],split[ 2 ] th = pool.getThread() t = th(target = ssh_fun,args = (ip,user,password,pool,db)) t.start() t_list.append(t) for i in t_list: i.join() db.close() |
分类:
python之路
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)