众所周知,python3多线程有threading,很好的支持了多线程,那么问题来了,为什么还需要线程池呢,其实很好回答,如果你要爬取网站有八百页,每页设置一个线程,难道能开启八百个么,光切换的时间也很高了吧。这时候就需要用到线程池,可以设置一个20的线程池,同时只有20个线程在运行,剩下的排队。直接上讲解
在threading中是没有线程池相关功能的,想要运行线程池需要自己重写,很明显像我这么懒不可能重写,而且自己编写线程池很难写的比较完美,还需要考虑复杂情况下的线程同步,很容易发生死锁。所以就用到了这个模块,
from concurrent.futures import ThreadPoolExecutor
从Python3.2开始,标准库为我们提供了concurrent.futures模块,它提供了ThreadPoolExecutor和ProcessPoolExecutor两个类,实现了对threading和multiprocessing的进一步抽象(线程池和进程池的唯一区别就是线程与进程,使用方法,内置函数等都完全一样),可以帮我们自动调度线程,省下大量时间,废话少说,直接上代码:
def thread_text(i,y):
time.sleep(i+y)
return i+y
if __name__ == '__main__':
executor = ThreadPoolExecutor(max_workers=5)
executor1 = executor.submit(thread_text, 1,1)
executor2 = executor.submit(thread_text, 2,1)
executor.shutdown()
首先需要实例化,然后在实例化的同时设置最大线程数,然后使用submit提交线程就ok啦,而且与threading.Thread 的线程不同的是,线程池不阻塞,立即执行,也就是说,不需要start来启动它,
而且想必有些同学已经看出我这个例子的常规例子的不同了,那就是我传了两个参数,这里也是为了区别threading.Thread的参数,众所周知,threading模块对方法传入参数是以元组的形式,比如我上面写的方法,如果用threading调用的话应该这么写threading.Thread(target=thread_text,args=(1,1)),而线程池则不是,如果翻过源码会发现ThreadPoolExecutor没有固定参数,只有*args和**kwargs,所以,线程池如果要传入多个参数,只需要按照参数位置跟在后面即可。
在介绍一下它的几个其他函数:
Future 提供了如下方法:
from concurrent.futures import ThreadPoolExecutor,as_completed
def thread_text(i,y):
time.sleep(i+y)
return i+y
if __name__ == '__main__':
executor = ThreadPoolExecutor(max_workers=5)
executor1 = executor.submit(thread_text, 1,1)
executor2 = executor.submit(thread_text, 2,1)
for i in as_completed([executor1,executor2]):
print(i.result())
executor.shutdown()
as_completed()方法是一个生成器,在没有任务完成的时候,会阻塞,在有某个任务完成的时候,会yield这个任务,就能执行for循环下面的语句,然后继续阻塞住,循环到所有的任务结束。从结果也可以看出,先完成的任务会先通知主线程。
除了submit,ThreadPoolExecutor还提供了map函数来添加线程,与常规的map类似,区别在于线程池的 map() 函数会为 iterables 的每个元素启动一个线程,以并发方式来执行 func 函数. 同时,使用map函数,还会自动获取返回值,相当于这样,executor.submit(func).result(),
看例子:
from concurrent.futures import ThreadPoolExecutor,as_completed
def thread_text(i,y):
time.sleep(i+y)
return i+y
if __name__ == '__main__':
executor = ThreadPoolExecutor(max_workers=5)
li2 = [1,1,1,1,1]
for i in executor.map(thread_text, range(1, 5), li2):
print(i)
print('-------')
executor.shutdown()
同时,输出顺序和列表的顺序相同,从代码可以看出,使用map函数使得代码更简洁,所以常规使用建议直接使用map。
from concurrent.futures import ThreadPoolExecutor,as_completed
def thread_text(i,y):
time.sleep(i+y)
return i+y
if __name__ == '__main__':
executor = ThreadPoolExecutor(max_workers=5)
executor1 = executor.submit(thread_text, 1,1)
executor2 = executor.submit(thread_text, 2,1)
for i in as_completed([executor1,executor2]):
if i.exception():
print(i.exception)
else:
print(i.result())
executor.shutdown()
另外,线程锁依然是用threading模块的,线程池这里并没有另外设置锁相关内容。
参考文档
[python] ThreadPoolExecutor线程池
Python线程池及其原理和使用(超级详细)