python 线程池 ThreadPoolExecutor

从Python3.2开始,标准库为我们提供了concurrent.futures 模块,它提供了 ThreadPoolExecutor (线程池)和 ProcessPoolExecutor (进程池)两个类。

相比 threading 等模块,该模块通过 submit 返回的是一个 future 对象,它是一个未来可期的对象,通过它可以获取某一个线程执行的状态或者某一个任务执行的状态及返回值:

  1. 主线程可以获取某一个线程(或者任务的)的状态,以及返回值。
  2. 当一个线程完成的时候,主线程能够立即知道。

1|0基础语法介绍


import time from concurrent.futures import ThreadPoolExecutor,wait,ALL_COMPLETED,FIRST_COMPLETED, as_completed def action(second): print(second) time.sleep(second) return second lists=[4,5,2,3] # 创建一个最大容纳数量为2的线程池 pool= ThreadPoolExecutor(max_workers=2) # 通过submit提交执行的函数到线程池中 all_task=[pool.submit(action, i) for i in lists] # 通过result来获取返回值 result=[i.result() for i in all_task] print(f"result:{result}") print("----complete-----") # 线程池关闭 pool.shutdown()

返回结果:

4 5 2 3 result:[4, 5, 2, 3] ----complete-----

2|0使用上下文管理器


可以通过 with 关键字来管理线程池,当线程池任务完成之后自动关闭线程池。

import time from concurrent.futures import ThreadPoolExecutor,wait,ALL_COMPLETED,FIRST_COMPLETED, as_completed def action(second): print(second) time.sleep(second) return second lists=[4,5,2,3] all_task = [] with ThreadPoolExecutor(max_workers=2) as pool: for second in lists: all_task.append(pool.submit(action, second)) result=[i.result() for i in all_task] print(f"result:{result}")
4 5 2 3 result:[4, 5, 2, 3]

3|0等待所有子线程完成


在需要返回值的场景下,主线程需要等到所有子线程返回再进行下一步,阻塞在当前。比如下载图片统一保存,这时就需要在主线程中一直等待,使用wait方法完成。

wait(fs, timeout=None, return_when=ALL_COMPLETED)

wait 接受三个参数:
fs: 表示需要执行的序列
timeout: 等待的最大时间,如果超过这个时间即使线程未执行完成也将返回
return_when:表示wait返回结果的条件,默认为 ALL_COMPLETED 全部执行完成再返回,可选 FIRST_COMPLETED

import time from concurrent.futures import ThreadPoolExecutor,wait,ALL_COMPLETED,FIRST_COMPLETED, as_completed lists=[4,5,2,3] all_task = [] with ThreadPoolExecutor(max_workers=2) as pool: for second in lists: all_task.append(pool.submit(action, second)) # 主线程等待所有子线程完成 wait(all_task, return_when=ALL_COMPLETED) print("----complete-----")
4 5 2 3 ----complete-----

4|0等待第一个子线程完成


wait 方法可以设置等待第一个子线程返回就继续执行,表现为主线程在第一个线程返回后便不会阻塞,继续执行下面的操作。

import time from concurrent.futures import ThreadPoolExecutor,wait,ALL_COMPLETED,FIRST_COMPLETED, as_completed def action(second): print(second) time.sleep(second) return second lists=[4,5,2,3] all_task = [] with ThreadPoolExecutor(max_workers=2) as pool: for second in lists: all_task.append(pool.submit(action, second)) # 主线程等待第一个子线程完成 wait(all_task, return_when=FIRST_COMPLETED) print("----complete-----")
4 5 2 ----complete----- 3

因为result方法是阻塞的,所以流程会在result这里阻塞直到所有子线程返回,相当于 ALL_COMPLETED 方法。

import time from concurrent.futures import ThreadPoolExecutor,wait,ALL_COMPLETED,FIRST_COMPLETED, as_completed def action(second): print(second) time.sleep(second) return second lists=[4,5,2,3] all_task = [] with ThreadPoolExecutor(max_workers=2) as pool: for second in lists: all_task.append(pool.submit(action, second)) # 主线程等待第一个子线程完成 wait(all_task, return_when=FIRST_COMPLETED) print("----first complete-----") result=[i.result() for i in all_task] print(f"result:{result}") print("----complete-----")
4 5 2 ----first complete----- 3 result:[4, 5, 2, 3] ----complete-----

5|0返回及时处理


如果不需要等待所有线程全部返回,而是每返回一个子线程就立刻处理,那么就可以使用as_completed获取每一个线程的返回结果。
as_completed() 方法是一个生成器,在没有任务完成的时候,会一直阻塞。当有某个任务完成的时候,会 yield 这个任务,就能执行 for 循环下面的语句,然后继续阻塞住,循环到所有的任务结束。同时,先完成的任务会先返回给主线程。

import time from concurrent.futures import ThreadPoolExecutor,wait,ALL_COMPLETED,FIRST_COMPLETED, as_completed def action(second): print(second) time.sleep(second) return second lists=[4,5,2,3] all_task = [] with ThreadPoolExecutor(max_workers=2) as pool: for second in lists: all_task.append(pool.submit(action, second)) for future in as_completed(all_task): print(f"{future.result()} 返回") print("----complete-----")
4 5 2 4 返回 3 5 返回 2 返回 3 返回 ----complete-----

6|0map


map 方法是对序列中每一个元素都执行 action 方法,主要有两个特点:

  1. 不需要将任务submit到线程池
  2. 返回结果的顺序和元素的顺序相同,即使子线程先返回也不会获取结果
map(fn, *iterables, timeout=None)

fn: 第一个参数 fn 是需要线程执行的函数;
iterables:第二个参数接受一个可迭代对象;
timeout: 第三个参数 timeout 跟 wait() 的 timeout 一样,但由于 map 是返回线程执行的结果,如果 timeout小于线程执行时间会抛异常 TimeoutError。

import time from concurrent.futures import ThreadPoolExecutor,wait,ALL_COMPLETED,FIRST_COMPLETED, as_completed def action(second): print(second) time.sleep(second) return second lists=[5,1,2,3] with ThreadPoolExecutor(max_workers=2) as pool: for result in pool.map(action, lists): print(f"{result} 返回")
5 1 2 3 5 返回 1 返回 2 返回 3 返回

可以看出返回结果和列表的结果一致,即使第2个元素只需要1s就能返回,也还是等待第一个5s线程返回只有才有结果。


__EOF__

本文作者goldsunshine
本文链接https://www.cnblogs.com/goldsunshine/p/16878089.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   金色旭光  阅读(23163)  评论(4编辑  收藏  举报
相关博文:
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
历史上的今天:
2019-11-15 读《阿里工程师的自我修养》我学到这几点
点击右上角即可分享
微信分享提示