python 线程池_python3中线程池

1.在使用多线程处理任务时也不是线程越多越好,由于在切换线程的时候,需要切换上下文环境,依然会造成cpu的大量开销。为解决这个问题,线程池的概念被提出来了。预先创建好一个较为优化的数量的线程,让过来的任务立刻能够使用,就形成了线程池。在python中,没有内置的较好的线程池模块,需要自己实现或使用第三方模块。下面是一个简单的线程池:

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.高级版线程池

"""

一个基于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)

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从数据库中进行读取):

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向数据库中插入数据:

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()

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值