Python多线程使用concurrent.futures.as_completed方法下的一个坑

本文介绍在Python爬虫项目中如何正确使用队列长度来匹配线程池的任务数,确保所有URL都能被正确处理。通过调整循环次数为队列的实际长度解决了因队列与循环次数不匹配导致的任务遗漏问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在使用Python3.7的时候,将获取到的url放入到队列Queue中,如下:这里的content_list是爬取到的列表

queue = Queue(list(content_list).__len__())

然后使用线程池ThreadPoolExecutor的时候将队列放到任务中,如下:

with ThreadPoolExecutor(max_workers=1) as executor:
            task_list = [executor.submit(self.get_detailed_info, queue.get()) for i in range(list(content_list).__len__())]
            for item in concurrent.futures.as_completed(task_list):
                print('done')

重点是在循环的时候,我这里写的是range(list(content_list).__len__()),因为我爬取的页面经过筛选,所以最后的队列中的url与原本的url数量不一致,所以最后在使用concurrent.futures.as_completed方法获取线程返回值的时候会获取不到,所以这里必须需要将循环的值设置为队列queue的长度,可以改为:range(queue.qsize()),如下

with ThreadPoolExecutor(max_workers=1) as executor:
            task_list = [executor.submit(self.get_detailed_info, queue.get()) for i in range(queue.qsize())]
            for item in concurrent.futures.as_completed(task_list):
                print('done')

这里具体原因,我认为是在executor.submit这方法中循环获取url数据的时候发现了我们的循环次数与实际不符合,所以不返回线程的数据,在此记录一下。

<think>好的,我现在需要帮用户介绍一下`concurrent.futures.as_completed`。用户之前询问了一个关于Python中数据库查询函数的问题,现在转向并发执行的模块,可能是在优化他们的程序性能。 首先,我得确定用户的使用场景。他们可能在处理需要并行执行多个数据库查询的任务,比如同时执行多个DRB_ID的查询,使用线程池或进程池来提高效率。用户之前用了`execute_query`函数,现在可能想用并发来加速多个查询的执行,所以才会问到`as_completed`。 接下来,我需要理解`concurrent.futures.as_completed`的作用。这个函数是用来处理多个future对象,按照完成的顺序逐个返回结果。用户可能想知道如何利用它来更高效地处理任务结果,尤其是当某些任务执行时间同,希望先完成的任务先处理。 然后,我需要考虑用户可能的疑问点。比如,`as_completed`和`map`方法的区别,为什么使用它,以及如何在实际中应用。同时,用户可能关心异常处理,比如某个任务失败会会影响其他任务,或者如何获取任务的返回值及异常信息。 另外,用户可能希望看到具体的例子,特别是和他们之前的代码结构相关的示例。比如,如何将`execute_query`函数结合到ThreadPoolExecutor中,使用`as_completed`来管理多个查询任务。 还要注意潜在的问题,比如线程安全,数据库连接是否支持多线程。用户之前的代码中每个线程独立创建连接,这可能已经解决了线程安全问题,但需要确认。此外,资源竞争或IO限制是否会影响并发性能,这也是需要考虑的因素。 最后,我需要组织回答结构,先解释函数的功能,然后对比其他方法,给出使用步骤和示例,再讨论注意事项和适用场景,确保用户能够逐步理解并应用到实际项目中。同时,提供优化建议,比如结合异常处理和动态获取列名,保持与之前回答的一致性。</think>以下是对 `concurrent.futures.as_completed` 的详细解析: --- ### **一、核心功能** $$as\_completed(future\_iterable, timeout=None) \rightarrow Iterator[Future]$$ - **作用**:监控一组并发任务(`Future`对象),按**完成顺序**而非提交顺序**迭代返回结果 - **核心价值**:在批量异步任务中优先处理先完成的任务,提升资源利用率 --- ### **二、与 map() 的对比** | 特性 | `as_completed` | `map()` | |---------------------|------------------------------------|-----------------------------| | **返回顺序** | 按任务完成顺序 | 按任务提交顺序 | | **结果处理时机** | 任意任务完成即可处理 | 等待所有任务完成 | | **适用场景** | 任务耗时差异大,需及时消费结果 | 任务耗时均匀,需保持顺序 | --- ### **三、典型使用步骤** 1. **创建任务池**:通过 `ThreadPoolExecutor` 或 `ProcessPoolExecutor` 提交任务 2. **收集Future对象**:将任务存入列表/集合 3. **遍历完成项**:用 `as_completed` 按完成顺序处理结果 4. **处理异常**:在遍历时捕获 `Future.exception()` ```python from concurrent.futures import ThreadPoolExecutor, as_completed def task(n): return n * n with ThreadPoolExecutor(max_workers=3) as executor: futures = [executor.submit(task, i) for i in range(5)] for future in as_completed(futures): result = future.result() # 获取已完成任务的结果 print(f"得到结果: {result}") ``` --- ### **四、结合您原有代码的案例** 假设需要并发执行多个DRB查询: ```python import logging from concurrent.futures import ThreadPoolExecutor, as_completed def run_parallel_queries(sql_template, param_list): with ThreadPoolExecutor(max_workers=4) as executor: # 提交所有查询任务 futures = { executor.submit(execute_query, sql_template, params): params["DRB_ID"] for params in param_list } # 按完成顺序处理 for future in as_completed(futures): drb_id = futures[future] try: df = future.result() logging.info(f"DRB {drb_id} 数据处理完成,共{len(df)}行") except Exception as e: logging.error(f"DRB {drb_id} 查询失败: {str(e)}") ``` --- ### **五、关键注意事项** 1. **超时控制**:通过 `timeout` 参数设置最大等待时间,超时引发 `TimeoutError` ```python for future in as_completed(futures, timeout=60): ``` 2. **异常隔离**:单个任务失败会中断其他任务,但需主动调用 `future.exception()` 检查 3. **资源竞争**:当任务涉及共享资源(如文件写入)时,需添加线程锁 4. **IO密集型优化**:数据库查询属于IO密集型任务,建议优先使用 `ThreadPoolExecutor` --- ### **六、性能优化建议** - **动态批处理**:根据历史耗时动态调整 `max_workers` 数量 - **结果缓存**:对已完成任务的CSV文件增加内存缓存,避免重复查询 - **进度跟踪**:结合 `tqdm` 库实现进度条显示 ```python from tqdm import tqdm for future in tqdm(as_completed(futures), total=len(futures)): ``` 需要进一步探讨如何与您的具体业务场景结合吗?例如动态调整线程数或异常恢复机制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值