使用原因
- 系统启动一个新线程的成本是比较高的,因为它涉及与操作系统的交互。
- 线程池在线程启动时即创建大量空闲的线程,程序只要将一个函数提交给线程池,线程池就会启动一个空闲的线程来执行它。当该函数执行结束后,该线程并不会死亡,而是再次返回到线程池中变成空闲状态,等待执行下一个函数。
- 使用线程池可以有效地控制系统中并发线程的数量。当系统中包含大量的并发线程时,会导致系统性能急剧下降,甚至导致Python解析器崩溃,而线程池的最大线程数参数可以控制系统中并发线程的数量不超过次数。
使用线程池
-
线程池的基类是
concurrent.futures
模块中的Executor
,Executor
提供了两个子类,即ThreadPoolExecutor
和ProcessPoolExecutor
,其中ThreadPoolExecutor
用于创建线程池,而ProcessPoolExecutor
用于创建进程池。 -
Executor
提供了如下常用方法:submit(fn, *args, \**kargs)
:将fn函数提交给线程池。map(func, *iterables, timeout=None, chunksize=1)
:该函数类似于全局函数map(func, *iterables)
,只是该函数会将启动多个线程,以异步方式立即对iterables执行map处理。shutdown(wait=True)
:关闭线程池。
-
程序将task函数submit给线程池后,submit方法会返回一个Future对象,
Future
类主要用于获取线程任务函数的返回值。Future
提供了如下方法:cancel()
:取消Future代表的线程任务。cancelled()
:返回Future代表的线程任务是否被成功取消。running()
:如果该Future代表的线程任务正在执行、不可被取消,该方法返回True。done()
:如果该Future代表的线程任务被成功取消或执行完成,则该方法返回True。result(timeout=None)
:获取该Future代表的线程任务最后返回的结果。如果Future代表的线程任务还未完成,该方法将会阻塞当前线程。exception(timeout=None)
:获取该Future代表的线程任务所引发的异常。如果该任务成功完成,没有异常,则该方法返回None。add_done_callback(fn)
:为该Future代表的线程任务注册一个“回调函数”,当该任务成功完成时,程序会自动触发该fn函数。
实例代码如下:
from concurrent.futures import ThreadPoolExecutor
import threading
def action(max):
my_sum = 0
for i in range(max):
print(threading.current_thread().name + ' ' + str(i))
my_sum += i
return my_sum
with ThreadPoolExecutor(max_workers=4) as pool:
future1 = pool.submit(action, 50)
future2 = pool.submit(action, 100)
def get_result(future):
print(future.result())
future1.add_done_callback(get_result)
future2.add_done_callback(get_result)
print('---------------------')
# results = pool.map(action, (50,100,150))
# print('------------')
# for r in results:
# print(r)