线程池在系统启动时即创建大量空闲的线程,程序只要将一个函数提交给线程池,线程池就会启动一个空闲的线程来执行它。当该函数执行结束后,该线程并不会死亡,而是再次返回到线程池中变成空闲状态,等待执行下一个函数。
有时候,我们无法知道
此外,使用线程池可以有效地控制系统中并发线程的数量。当系统中包含有大量的并发线程时,会导致系统性能急剧下降,甚至导致 Python 解释器崩溃,而线程池的最大线程数参数可以控制系统中并发线程的数量不超过此数。
比如我在U盘扫描病毒那里已经实现,假如定义了5个线程,扫描20个文件夹,此时会同时扫描5个文件夹,还剩下15个,被扫的5个文件夹中,有一个文件夹比较小,会比较快被扫完,假如过了5秒后,这个文件夹被扫描完了,此时有一个线程是空着的,则会去扫第6个文件夹,此时还是5个线程,此时就剩下14个文件夹未扫咯,这些操作都是自动调度的,我们不用手动干预,创建多线程还是有挺多方式的,下面分别介绍。
1、threadpool实现线程池
#encoding:utf-8
import time
import threadpool
import random
import threading
def sayhello(str): #执行方法
sleep_seconds=random.randint(1, 3)
print '线程名称:%s,参数:%s,睡眠时间:%s'%(threading.current_thread().name,str,sleep_seconds)
time.sleep(sleep_seconds)
name_list =['aa','bb','cc','dd'] #总共需要执行4个线程
start_time = time.time() #开始时间
pool = threadpool.ThreadPool(2) #创建2个线程
#创建请求列表
requests = threadpool.makeRequests(sayhello, name_list)
for req in requests:
pool.putRequest(req) #将每个请求添加到线程池中
pool.wait() #等待线程执行完后再执行主线程
print '总共运行时间:%d second'% (time.time()-start_time)
结果如下:
我们可以看到,线程池总共创建了2个线程去执行4个任务,Thread-1睡眠时间为3秒,Thread-2睡眠时间为2秒,也就是说,Thread-2会首先执行完,之后线程池会自动调度下一个任务赋给Thread-2,这样就实现了线程池控制线程数量并自动调度线程的功能!
2、ThreadPoolExecutor实现线程池
threadpool是一个比较老的模块了,现在虽然还有一些人在用,但已经不再是主流了,关于python多线程,现在已经开始步入未来(future模块)了,使用concurrent.futures模块,这个模块是python3中自带的模块,但是,python2.7以上版本也可以安装使用,具体使用方式如下(Python2.7安装:pip install futures)
#encoding:utf-8
from concurrent.futures import ThreadPoolExecutor
import threading
import random
import time
#线程任务
def task(i):
sleep_seconds = random.randint(1, 3) #随机睡眠时间
print '线程名称:%s,参数:%s,睡眠时间:%s' % (threading.current_thread().name, i, sleep_seconds)
time.sleep(sleep_seconds) #定义睡眠时间
#创建一个包含2条线程的线程池
pool = ThreadPoolExecutor(max_workers = 2) #定义两个线程
#向线程池提交5个任务
for i in range(5):
future1 = pool.submit(task, i) #任务加入线程池
pool.shutdown()
运行结果:
3、自定义线程池
有时候,我们可以手动构建线程池,不依赖上面的模块
任务获取和执行:
(1)任务加入队列,等待线程来获取并执行。
(2)按需生成线程,每个线程循环取任务。
线程销毁:
(1)获取任务是终止符时,线程停止。
(2)线程池close()时,向任务队列加入和已生成线程等量的终止符。
(3)线程池terminate()时,设置线程下次任务取到为终止符。
运行流程图如下:
# -*- coding:utf-8 -*-
import Queue
import threading
import contextlib
StopEvent = object()
class ThreadPool(object):
def __init__(self, max_num):
self.q = Queue.Queue()#存放任务的队列
self.max_num = max_num#最大线程并发数
self.terminal = False#如果为True 终止所有线程,不再获取新任务
self.generate_list = [] #已经创建的线程
self.free_list = []#闲置的线程,当无空闲的线程,且有新任务调用了run方法,则需要创建新的线程
def run(self, func, args, callback=None):
"""
线程池执行一个任务
:param func: 任务函数
:param args: 任务函数所需参数
:param callback: 任务执行失败或成功后执行的回调函数,回调函数有两个参数1、任务函数执行状态;2、任务函数返回值(默认为None,即:不执行回调函数)
:return: 如果线程池已经终止,则返回True否则None
"""
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):
"""
创建一个线程
"""
t = threading.Thread(target=self.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 = func(*arguments) #运行函数,把结果赋值给result
status = True #运行结果是否正常
except Exception as e:
status = False #不正常
result = e #结果为错误信息
if callback is not None: # 是否有回调函数
try:
callback(status, result) #执行回调函数
except Exception as e:
pass
if self.terminal: # 默认为False ,如果调用terminal方法
event = StopEvent #停止信号
else:
# self.free_list.append(current_thread) #执行完毕任务,添加到闲置列表
# event = self.q.get() #获取任务
# self.free_list.remove(current_thread) #获取到任务之后,从闲置里删除
with self.worker_state(self.free_list,current_thread):
event = self.q.get()
else:
self.generate_list.remove(current_thread) #如果收到终止信号,就从已创建的列表删除
def close(self): #终止线程
num = len(self.generate_list) #获取总已创建的线程
while num:
self.q.put(StopEvent) #添加停止信号,有几个线程就添加几个
num -= 1
# 终止线程(清空队列)
def terminate(self):
self.terminal = True #更改为True,
while self.generate_list: #如果有已创建线程存活
self.q.put(StopEvent) #有几个就发几个信号
self.q.empty() #清空队列
@contextlib.contextmanager
def worker_state(self,free_list,current_thread):
free_list.append(current_thread)
try:
yield
finally:
free_list.remove(current_thread)
import time
import random
def work(i):
sleep_sec=random.randint(1,3)
print(threading.current_thread().name+ ' '+str(i)+' '+str(sleep_sec))
time.sleep(sleep_sec)
pool = ThreadPool(3)
for item in range(7):
pool.run(func=work, args=(item,))
# pool.terminate()
pool.close()