python 多线程系列—线程池 ThreadPoolExecutor(八)

本贴最后更新于 757 天前,其中的信息可能已经事过景迁

一、为什么要用线程池

1、启动一个新的线程这个过程因为涉及与操作系统交互,会大量消耗系统资源,导致系统性能降低。此时使用线程池可以很好地提升性能,如果短时间内需要使用大量线程,更应该考虑使用线程池。
2、使用线程池可以有效地控制系统中并发线程的数量。当系统中包含有大量的并发线程时,会导致系统性能急剧下降,甚至导致 Python 解释器崩溃,而线程池的最大线程数参数可以控制系统中并发线程的数量不超过此数。
3、线程不是开的越多越好,开多了可能导致系统性能下降。

二、线程池工作原理

1、线程池在系统启动时即创建大量空闲的线程,程序只要将一个函数提交给线程池,线程池就会启动一个空闲的线程来执行它。
2、当该函数执行结束后,该线程并不会死亡,而是再次返回到线程池中变成空闲状态,在线程池等待任务执行。
3、本质是创建多个线程,通过标记线程状态来控制线程不销毁,有任务的时候再给空闲的线程派活。

三、线程池的优点

1、提升性能:因为减去了大量新建、终止线程的开销,重用了线程资源
2、适用场景:适合处理突发性大量请求或者需要大量线程完成任务、但实际任务处理时间较短
3、防御功能:能有效避免系统因为创建线程太多,而导致系统负荷过大响应变慢等问题
4、代码优势:使用线程池的语法比自己新建现场执行线程代码更简洁

四、ThreadPoolExecutor(多线程类)

Executor类提供的方法详解
1、submit(fn, *args, **kwargs)
作用:fn 函数提交给线程池
参数:
将 fn 函数提交给线程池。
*args 代表传给 fn 函数的参数,
*kwargs 代表以关键字参数的形式为 fn 函数传入参数
返回值:程序将fn函数提交(submit)给线程池后,submit 方法会返回一个 Future 对象,Future 类主要用于获取线程任务函数的返回值。由于线程任务会在新线程中以异步方式执行,因此,线程执行的函数相当于一个“将来完成”的任务,所以 Python 使用 Future 来代表。

2、map(func, *iterables, timeout=None, chunksize=1)
作用:该函数类似于全局函数 map(func, *iterables),只是该函数将会启动多个线程,以异步方式立即对 iterables 执行 map 处理。
参数:
*iterables 代表传给 fn 函数的参数,可迭代对象
timeout=None:超时时间
chunksize=1:ProcessPoolExecutor使用,分块方式传递给子进程
3、shutdown(wait=True):类似于join()作用,等待线程池中所有任务执行完毕后,主线程再继续往下执行
参数:
wait=True:主线程等待子线程执行结束再结束,内部调用了join()方法

Future类提供了如下方法[几乎不用]
1、cancel():取消Future代表的线程任务。如果任务正在执行不可取消,则该方法返回 False;否则,程序会取消该任务,并返回 True。
2、cancelled():返回 Future 代表的线程任务是否被成功取消。
3、running():如果该 Future 代表的线程任务正在执行、不可被取消,该方法返回 True。
4、done():如果该 Funture 代表的线程任务被成功取消或执行完成,则该方法返回 True。
5、result(timeout=None):获取该 Future 代表的线程任务最后返回的结果。如果 Future 代表的线程任务还未完成,该方法将会阻塞当前线程,其中 timeout 参数指定最多阻塞多少秒。
6、exception(timeout=None):获取该 Future 代表的线程任务所引发的异常。如果该任务成功完成,没有异常,则该方法返回 None。
7、add_done_callback(fn):为该 Future 代表的线程任务注册一个“回调函数”,当该任务成功完成时,程序会自动触发该 fn 函数。函数会自动传递Future返回的参数作为函数的入参。

五、多线程使用

用法1:map函数,按顺序提交任务,结果和入参是顺序对应的

from concurrent.futures import ThreadPoolExecutor
def test_01(x,y):
    return x+y
with ThreadPoolExecutor() as pool:
    result = pool.map(test_01,[1,2,3],[1,2,3])
    for res in result:
        print(res)

用法2:future模式,更强大,随时提交任务,用as_completed顺序是不定的,哪个任务先执行完成就返回哪个任务执行结果

from concurrent.futures import ThreadPoolExecutor,as_completed
def test_01(x,y):
    return x+y
with ThreadPoolExecutor() as pool:
    test_dict = {"key1":"val1","key2":"val2"}
    #执行结果直接在这个list中
    futures = [pool.submit(test_01,x,y) for x,y in test_dict.items()]
    pool.shutdown(wait=True) #等待所有线程执行结束再去遍历结果
    #按执行顺序返回结果
    for fut in futures:
        print(fut.result())
    #用as_completed顺序是不定的,哪个任务先执行完成就返回哪个任务执行结果
    print("="*50)
    for fut in as_completed(futures):
        print(fut.result())

六、add_done_callback(fn):回调函数

1、Fn代表的线程任务注册一个“回调函数”当该任务成功完成时,程序会自动触发该 fn 函数。
2、函数会自动传递Future返回的参数作为函数的入参。
3、一般用于分步处理的场景

#干活函数
def test_01():
    return x+y
#回调函数
def save(response):
    result = response.result()
	print("对结果进行其他操作")

with ThreadPoolExecutor() as pool:
    test_dict = {"key1":"val1","key2":"val2"}
    #执行结果直接在这个list中
    for x,y in test_dict.items():
    	future = pool.submit(test_01,x,y)
        #将执行结果返回fn2处理,test_01要有返回值
        future.add_done_callback(fn2)
3 回帖
请输入回帖内容 ...
  • AMuBai

    海励大佬牛批!!!666!!!

  • 其他回帖
  • BlackStar
    该回帖仅作者和楼主可见
  • xiu

    怒扣666 不愧是海励老师!!1