Python自定义线程池,这么高效,是不是开了挂?

目录

1、线程池基础 🏗️

1.1 线程池概念与优势

1.2 Python标准库concurrent.futures简介

示例代码:使用ThreadPoolExecutor执行简单任务

2、利用ThreadPoolExecutor定制 🎛️

2.1 创建自定义线程池类

示例代码:自定义ThreadPoolExecutor子类

2.2 设置线程池参数与任务提交

示例代码:配置线程池并提交任务

2.3 错误处理与结果回调

示例代码:使用add_done_callback处理异常

3、从零开始实现线程池 🌀

3.1 设计线程池架构

3.2 实现任务队列与工作线程

任务队列实现

工作线程实现

3.3 线程同步与安全控制

4、集成异步IO与线程池 🌐

4.1 asyncio与线程池混用技巧

示例代码:混合使用asyncio与ThreadPoolExecutor

4.2 提升I/O密集型任务性能

示例代码:异步并发下载网页

4.3 实战案例:并发下载与处理

示例代码:并发下载图片并转换为灰度

5、优化与监控策略 📊

5.1 动态调整线程池大小

示例代码:基于任务队列长度动态调整线程池

5.2 监控线程池状态与性能指标

实施方案:

5.3 日志记录与异常报警

示例代码:集成日志记录与异常处理

6、总结 🎯



1、线程池基础 🏗️

1.1 线程池概念与优势

线程池是一种软件设计模式,用于管理多个线程的创建、执行、销毁等生命周期 ,从而提高程序性能与资源利用率。它预先创建一定数量的工作线程并保持就绪状态 ,当有任务到达时 ,线程池会从队列中取出任务并分配给空闲线程执行 ,执行完毕后线程不会立即销毁而是回到池中等待下一轮任务。这一机制有效减少了频繁创建和销毁线程的开销,提升了系统的响应速度和吞吐量。

优势包括:

  • • 资源高效:减少线程创建与销毁的开销。

  • • 控制并发:通过线程池大小限制并发任务数,防止资源过度消耗。

  • • 管理便利:统一调度任务,便于监控与控制。

  • • 提升响应:复用已有线程,加快任务处理速度。

1.2 Python标准库concurrent.futures简介

concurrent.futures模块自Python 3.2起被引入,为异步执行提供了高层次的接口,包括两种主要的执行器:ThreadPoolExecutor用于多线程并发,而ProcessPoolExecutor则用于多进程。对于自定义线程池的需求,ThreadPoolExecutor是理想的起点。

ThreadPoolExecutor允许开发者轻松创建线程池,提交任务 ,并获取结果,支持同步等待(result())和异步获取结果(add_done_callback()), 提供了异常处理机制以及对任务完成情况的追踪。

示例代码:使用ThreadPoolExecutor执行简单任务

下面是一个使用ThreadPoolExecutor的示例,展示了如何创建一个线程池,提交任务,并获取结果。

import concurrent
from concurrent.futures import ThreadPoolExecutor
import time

def long_running_task(n):
    """模拟耗时操作"""
    time.sleep(n)
    return f"Task {n} done after {n} seconds."

# 创建一个包含4个线程的线程池
with ThreadPoolExecutor(max_workers=4)as executor:
    # 提交任务到线程池
    futures ={executor.submit(long_running_task, n)for n in range(5)}

# 收集结果
for future in concurrent.futures.as_completed(futures):
    print(future.result())

此段代码中,long_running_task模拟了一个耗时操作,通过线程池提交了5个这样的任务。每个任务在完成后打印其结果,由于线程池最大工作者数为4,因此前四个任务会立即开始执行 ,最后一个任务会在某个线程释放后继续。

请注意,实际应用中应根据具体需求调整线程池大小及任务细节。

2、利用ThreadPoolExecutor定制 🎛️

2.1 创建自定义线程池类

为了更好地控制线程池的行为,我们可以创建一个自定义的ThreadPoolExecutor子类。这不仅允许我们覆盖默认行为,还能添加额外的功能,如更详细的日志记录、错误处理策略以及线程池状态的监控。

示例代码:自定义ThreadPoolExecutor子类

下面的代码展示了如何创建一个自定义的ThreadPoolExecutor子类 ,其中增加了初始化时的参数验证以及更丰富的日志记录功能。

from concurrent.futures import ThreadPoolExecutor
import logging

class CustomThreadPoolExecutor(ThreadPoolExecutor):
    def __init__(self, max_workers=None, thread_name_prefix='', initializer=None, initargs=()):
        if max_workers is None or max_workers <=0:
            raise ValueError("max_workers must be greater than 0")

        super().__init__(max_workers=max_workers,thread_name_prefix=thread_name_prefix, initializer=initializer,initargs=initargs)

        self.logger = logging.getLogger(__name__)
        self.logger.setLevel(logging.INFO)

    def submit(self, fn, *args, **kwargs):
        future =super().submit(fn,*args,**kwargs)
        self.logger.info(f"Submitted task: {fn.__name__} with args: {args} and kwargs: {kwargs}")
        return future

# 使用自定义线程池执行任务
with CustomThreadPoolExecutor(max_workers=4, thread_name_prefix="CustomThread")as executor:
    futures =[executor.submit(pow, base, exponent)for base, exponent in[(2,10),(3,5)]]
for future in futures:
    print(f"Result: {future.result()}")

输出:

Result: 1024
Result: 243

 

在这个示例中,我们首先检查max_workers是否有效,然后初始化父类ThreadPoolExecutor。我们还设置了一个logger,用于记录任务的提交情况。submit方法被重写 ,以便在任务提交时记录相关信息。

2.2 设置线程池参数与任务提交

在创建线程池时,可以通过参数max_workers来指定线程池中的最大线程数。此外,还可以通过thread_name_prefix来设置线程名前缀,便于调试和日志分析。

示例代码:配置线程池并提交任务

下面的代码示例展示了如何配置线程池并提交多个任务,同时展示了如何使用as_completed函数来获取已完成的任务结果。

from concurrent.futures import ThreadPoolExecutor, as_completed

def calculate_factorial(number):
    factorial =1
    for i in range(1, number +1):
        factorial *= i
        return factorial

with ThreadPoolExecutor(max_workers=5)as executor:
    tasks =[executor.submit(calculate_factorial, n)for n in range(1,6)]
    for future in as_completed(tasks):
            result = future.result()
print(f"The factorial of {future._args[0]} is {result}")

这个示例中 ,我们定义了一个calculate_factorial函数来计算阶乘,然后使用ThreadPoolExecutor创建了一个包含5个线程的线程池。我们提交了5个任务,分别计算1到5的阶乘 ,并使用as_completed来迭代并打印出每个任务的结果。

2.3 错误处理与结果回调

在处理

  • 12
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Python线程池并未加快运行的原因是因为Python解释器中存在一个全局解释器锁(GIL,Global Interpreter Lock)。这个锁的作用是确保在同一时刻只有一个线程可以执行Python字节码。即使在多核CPU上运行,由于GIL的存在,Python解释器在同一时刻只能运行一个线程,无法充分利用多核CPU的优势。因此,尽管使用了线程池,但实际上只有一个线程在执行,无法加快程序的运行速度。 需要注意的是,GIL只存在于CPython解释器中,其他一些Python解释器,如Jython和IronPython,没有GIL的限制,可以实现真正的并行执行。如果想要充分利用多核CPU,可以考虑使用其他Python解释器或者使用多进程而不是多线程来进行并行计算。 此外,需要注意的是,当线程中涉及到大量的IO操作时,线程池仍然可以发挥一定的优势,因为在IO操作期间,线程可以释放GIL,让其他线程执行。因此,对于IO密集型的任务,线程池仍然可以提高程序的效率。而对于计算密集型的任务,由于GIL的限制,线程池并不能实现加速运行的效果。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [python线程退出](https://blog.csdn.net/weixin_39850981/article/details/109934040)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

图灵学者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值