为什么要使用线程池:
1.系统启动一个新线程的成本是比较高的,因为它涉及与操作系统的交互。线程池因为其内线程可以反复利用的特性,能够很好地提高性能,尤其当程序需要创建大量生命周期很短的线程时。
2.一味因为任务数量的增加而增加线程数量,最终会导致线程数量的失控,不仅系统性能会因此急剧下降,甚至会导致Python解释器崩溃。而线程池可以有效地控制系统中并发线程的数量。
如何创建线程池:
创建线程池可以使用concurrent.futures模块中的Executor,Executor提供两个子类:ThreadPoolExecutor 和 ProcessPoolExecutor,其中ThreadPoolExecutor用于创建线程池,而ProcessPoolExecutor用于创建进程池。
1.Executor提供的方法:
- submit(func,args,kwargs) :将func函数提交给线程池。
- map(func,iterables,timeout=None.chunksize=1)
:类似于Python的内置map函数,该函数将会启动多个线程,以异步方式立即对iterables进行map处理。 - shutdown(wait=True):关闭线程池。
<注意>
1.当程序调用submit()方法将一个func提交给线程池后,submit()方法会返回一个Future对象,Future类主要用于获取线程任务函数的返回值。
2.创建一个线程时,可以指定线程池的最大线程容量,如果不指定那么默认是CPU的核数。另外,创建一个线程池,线程池中自动会创建出指定数目的空闲线程,当我们将一个函数传给线程池后,线程池会自动选择一个空闲线程来执行这个函数。执行完毕后,该线程继续成为空闲状态,而并不会死去。
3.当用shutdown()方法关闭线程池时,仍在运行的线程将会等待其运行完毕后再去关闭,彻底关闭后,线程池中的所有线程都将死亡。
2.Future对象提供的方法:
- cancel()
:取消该Future代表的线程任务。如果该任务正在执行,不可取消,此时该方法返回False,否则程序会取消该任务并返回True。 - cancelled() :返回Future代表的线程任务是否被成功取消。
- running() :如果该Future代表的线程任务正在执行,不可被取消,该方法返回True。
- done():如果该 Funture 代表的线程任务被成功取消或执行完成,则该方法返回 True。
- result(timeout):获取该 Future 代表的线程任务最后返回的结果。如果 Future
代表的线程任务还未完成,该方法将会阻塞当前线程。 - exception(timeout):获取该 Future 代表的线程任务所引发的异常。如果该任务成功完成,没有异常,则该方法返回
None。 - add_done_callback(func):为该 Future
代表的线程任务注册一个“回调函数”,当该任务成功完成时,程序会自动触发该 func 函数。
3.线程池创建使用示例
示例1:
from concurrent.futures import ThreadPoolExecutor
import threading
from time import sleep
def info(a,b):
for i in range(b):
a += i
return a
pool = ThreadPoolExecutor(2)
f1 = pool.submit(info,2,3)
f2 = pool.submit(info,4,7)
if f1.done():
sleep(2)
print('f1 Result is %d' % (f1.result()))
f2.cancel()
if f2.cancelled():
pool.shutdown()
else:
if f2.done():
sleep(2)
print('f2 Result is %d' % (f2.result()))
pool.shutdown()
运行结果:
f1 Result is 5
f2 Result is 25
示例2:
from concurrent.futures import ThreadPoolExecutor
def info(a,b):
for i in range(b):
a += i
return a
with ThreadPoolExecutor(max_workers=2) as pool:
f1 = pool.submit(info,2,3)
f2 = pool.submit(info,4,7)
def get_result(future):
print('Result is %d' % (future.result()))
f1.add_done_callback(get_result)
f2.add_done_callback(get_result)
运行结果:
Result is 5
Result is 25
<分析>
result()函数被调用后,如果不能立即得到返回结果,当前线程会被挂起,比如在示例1中,调用result()方法后,如果info()函数的执行需要很长时间,那么主线程就会被挂起,有什么方法在调用result()方法后即使无法立刻得到结果但是当前线程也不会被挂起呢?
那就需要用到add_done_callback()方法,该方法给future对象添加一个回调函数,添加了回调函数的future对象即使在调用result()方法后无法立即得到返回结果也不会阻塞当前线程,因为回调函数会帮result()把执行结果输出,回调函数会一直等待该线程完成,之后带回结果。
示例3:
from concurrent.futures import ThreadPoolExecutor
def info(a,b):
for i in range(b):
a += i
return a
with ThreadPoolExecutor(2) as pool:
results = pool.map(info,(2,4),(3,7)) #第一个括号里的是所有线程的第一个参数,以此类推
for r in results:
print(r)
运行结果:
5
25
<分析>
传入两组参数,map()方法会自动创建两个线程去执行,创建的线程将会并发执行。