《从零构建 Python 线程池:深入理解 Work Queue、条件变量与并发设计的本质》

2025博客之星年度评选已开启 10w+人浏览 3.1k人参与

《从零构建 Python 线程池:深入理解 Work Queue、条件变量与并发设计的本质》

一、开篇:为什么要手写一个线程池?

Python 自 1991 年诞生以来,以其简洁优雅的语法、强大的生态系统和“胶水语言”的灵活性,迅速成为 Web 开发、数据科学、人工智能、自动化等领域的核心语言。随着业务规模增长、实时性需求提升,并发编程成为 Python 开发者必须掌握的能力。

你可能已经使用过:

  • concurrent.futures.ThreadPoolExecutor
  • multiprocessing.Pool
  • asyncio

这些工具极大降低了并发编程的门槛,但也让很多开发者忽略了底层原理。

为什么要手写线程池?

  • 彻底理解线程池的设计思想
  • 掌握工作队列(Work Queue)的生产者-消费者模型
  • 理解条件变量(Condition)的作用
  • 学会构建可扩展的并发框架
  • 在面试、系统设计、性能优化中具备底层能力

更重要的是:

当你能手写线程池时,你对 Python 并发的理解将从“会用”跃升到“精通”。


二、基础回顾:Python 并发的核心概念

为了让初学者也能顺利阅读,我们先快速回顾 Python 并发的基础知识。

1. 线程与 GIL

Python 的线程由操作系统调度,但 CPython 有 GIL(全局解释器锁),导致:

  • 多线程适合 I/O 密集任务
  • 多线程不适合 CPU 密集任务

但线程池的设计思想与 GIL 无关,它是通用的并发模型。

2. 工作队列(Work Queue)

线程池的核心思想:

  • 主线程不断向队列提交任务
  • 工作线程不断从队列取任务执行
  • 队列为空时,工作线程等待
  • 队列有任务时,工作线程被唤醒

这就是典型的生产者-消费者模型

3. 条件变量(Condition)

条件变量用于:

  • 线程等待某个条件成立
  • 其他线程改变条件后发出通知

线程池中:

  • 工作线程等待“队列非空”
  • 主线程提交任务后通知工作线程

三、基础语法示例:装饰器与函数调用时间

为了保持文章结构一致,我们插入一个基础示例:

import time

def timer(func):
    def wrapper(*args, **kwargs):
        start = time.time()
        result = func(*args, **kwargs)
        end = time.time()
        print(f"{func.__name__} 花费时间:{end - start:.4f}秒")
        return result
    return wrapper

@timer
def compute_sum(n):
    return sum(range(n))

print(compute_sum(1000000))

四、正式进入主题:手写一个线程池

我们将从最小可用版本开始,一步步扩展。


五、第一步:实现一个线程安全的工作队列(Work Queue)

我们需要:

  • 一个任务队列(list 或 deque)
  • 一个锁(Lock)
  • 一个条件变量(Condition)

代码:WorkQueue 实现

import threading
from collections import deque

class WorkQueue:
    def __init__(self):
        self.queue = deque()
        self.lock = threading.Lock()
        self.not_empty = threading.Condition(self.lock)

    def put(self, item):
        with self.not_empty:
            self.queue.append(item)
            self.not_empty.notify()  # 通知等待的线程

    def get(self):
        with self.not_empty:
            while not self.queue:
                self.not_empty.wait()  # 队列为空,等待
            return self.queue.popleft()

关键点解析

  • Condition(self.lock):条件变量绑定锁
  • wait():释放锁并阻塞,直到被 notify
  • notify():唤醒一个等待线程
  • while not queue:防止虚假唤醒

六、第二步:实现 Worker 线程

Worker 线程需要:

  • 无限循环从队列取任务
  • 执行任务
  • 支持线程池关闭

代码:Worker 实现

class Worker(threading.Thread):
    def __init__(self, work_queue, pool):
        super().__init__()
        self.work_queue = work_queue
        self.pool = pool
        self.daemon = True  # 主线程退出时自动退出

    def run(self):
        while True:
            task = self.work_queue.get()
            if task is None:  # 收到关闭信号
                break
            func, args, kwargs = task
            try:
                func(*args, **kwargs)
            except Exception as e:
                print("任务执行异常:", e)

七、第三步:实现 ThreadPool(线程池主体)

线程池需要:

  • 初始化多个 Worker
  • 提供 submit() 方法提交任务
  • 提供 shutdown() 方法关闭线程池

代码:ThreadPool 实现

class ThreadPool:
    def __init__(self, num_workers=4):
        self.work_queue = WorkQueue()
        self.workers = []
        self.num_workers = num_workers
        self._init_workers()

    def _init_workers(self):
        for _ in range(self.num_workers):
            worker = Worker(self.work_queue, self)
            worker.start()
            self.workers.append(worker)

    def submit(self, func, *args, **kwargs):
        self.work_queue.put((func, args, kwargs))

    def shutdown(self, wait=True):
        # 向每个 worker 发送关闭信号
        for _ in self.workers:
            self.work_queue.put(None)

        if wait:
            for worker in self.workers:
                worker.join()

八、完整可运行版本:手写线程池

下面是完整代码,可直接运行:

import threading
from collections import deque

class WorkQueue:
    def __init__(self):
        self.queue = deque()
        self.lock = threading.Lock()
        self.not_empty = threading.Condition(self.lock)

    def put(self, item):
        with self.not_empty:
            self.queue.append(item)
            self.not_empty.notify()

    def get(self):
        with self.not_empty:
            while not self.queue:
                self.not_empty.wait()
            return self.queue.popleft()


class Worker(threading.Thread):
    def __init__(self, work_queue, pool):
        super().__init__()
        self.work_queue = work_queue
        self.pool = pool
        self.daemon = True

    def run(self):
        while True:
            task = self.work_queue.get()
            if task is None:
                break
            func, args, kwargs = task
            try:
                func(*args, **kwargs)
            except Exception as e:
                print("任务执行异常:", e)


class ThreadPool:
    def __init__(self, num_workers=4):
        self.work_queue = WorkQueue()
        self.workers = []
        self.num_workers = num_workers
        self._init_workers()

    def _init_workers(self):
        for _ in range(self.num_workers):
            worker = Worker(self.work_queue, self)
            worker.start()
            self.workers.append(worker)

    def submit(self, func, *args, **kwargs):
        self.work_queue.put((func, args, kwargs))

    def shutdown(self, wait=True):
        for _ in self.workers:
            self.work_queue.put(None)
        if wait:
            for worker in self.workers:
                worker.join()

九、测试线程池:执行多个任务

import time

def task(n):
    print(f"开始任务 {n}")
    time.sleep(1)
    print(f"结束任务 {n}")

pool = ThreadPool(num_workers=3)

for i in range(10):
    pool.submit(task, i)

pool.shutdown()

输出示例:

开始任务 0
开始任务 1
开始任务 2
结束任务 0
开始任务 3
...

十、进阶优化:支持 Future、返回值、异常捕获

如果你愿意,我可以继续扩展:

  • 支持 Future 对象
  • 支持任务返回值
  • 支持任务优先级
  • 支持最大队列长度
  • 支持超时
  • 支持线程复用统计

这些都是生产级线程池需要的能力。


十一、案例实战:用手写线程池构建一个小型爬虫

import requests

def fetch(url):
    resp = requests.get(url)
    print(url, len(resp.text))

urls = [
    "https://www.python.org",
    "https://www.github.com",
    "https://www.baidu.com",
] * 3

pool = ThreadPool(5)

for url in urls:
    pool.submit(fetch, url)

pool.shutdown()

十二、最佳实践总结

  • 使用条件变量实现线程同步
  • 使用队列实现生产者-消费者模型
  • 使用 None 作为关闭信号
  • 使用 daemon 线程避免阻塞退出
  • 使用 join() 等待线程结束

十三、前沿视角:Python 并发的未来

  • PEP 703(无 GIL Python)正在推进
  • asyncio 已成为异步标准
  • FastAPI、Quart 等框架推动异步生态
  • Rust + Python(PyO3)成为高性能趋势
  • 多线程未来可能真正实现并行

十四、总结与互动

我们从 Python 基础讲起,一步步构建了:

  • 工作队列
  • 条件变量同步
  • Worker 线程
  • 线程池主体
  • 完整可运行线程池

你不仅学会了“如何写”,更理解了“为什么这样写”。

我很想听听你的想法:

  • 你希望我继续扩展支持 Future 的版本吗?
  • 你想看“手写协程调度器”或“手写进程池”吗?

告诉我你的方向,我可以继续为你构建更完整的并发体系文章。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

铭渊老黄

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

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

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

打赏作者

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

抵扣说明:

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

余额充值