Python 并发编程实战学习笔记

在 Python 开发中,并发编程是一个绕不开的话题。无论是处理高并发的 Web 请求,还是批量处理数据,掌握多线程都是进阶必经之路。

本文将从基础原理实战代码并发模型对比 以及面试高分话术四个维度,带你彻底搞懂 Python 并发。


核心概念:GIL 与线程模型

在深入代码之前,必须先理解 Python 的"紧箍咒"------GIL

什么是 GIL?

GIL 全称是全局解释器锁。在 CPython 解释器中,同一时刻只能有一个线程在 CPU 上执行字节码。

GIL 的影响
  • CPU 密集型任务 (如复杂计算、图像处理):多线程无效 ,甚至因为线程切换导致更慢。
    • 解决方案 :使用多进程multiprocessing)。
  • I/O 密集型任务 (如文件读写、网络请求、数据库操作):多线程非常有效 。因为在等待 I/O 时,GIL 会释放,允许其他线程运行。
    • 解决方案 :使用多线程threading)。

️实战:如何使用 ThreadPoolExecutor

在 Python 3 中,我们不再推荐直接使用 threading.Thread,而是使用更高级的 concurrent.futures 模块,特别是 ThreadPoolExecutor

场景一:批量处理(使用 map)

如果你有一堆数据需要并行处理,且不关心谁先做完,只在乎结果的顺序,map 是最简洁的选择。

python 复制代码
import time
from concurrent.futures import ThreadPoolExecutor

def process_file(filename):
    print(f"正在处理: {filename}")
    time.sleep(1)  # 模拟耗时 I/O
    return f"{filename}_结果"

file_list = [f"file_{i}.txt" for i in range(5)]

# max_workers=3 表示同时最多 3 个线程
with ThreadPoolExecutor(max_workers=3) as executor:
    # map 会按 file_list 的顺序返回结果
    results = executor.map(process_file, file_list)
    for res in results:
        print(res)
场景二:精细控制(使用 submit)

如果你需要传递多个参数,或者需要"谁先做完先处理谁",甚至需要单独捕获异常,请使用 submit

python 复制代码
from concurrent.futures import ThreadPoolExecutor, as_completed
import time

def task(n):
    time.sleep(n) # 模拟耗时不同
    return f"任务{n}完成"

with ThreadPoolExecutor(max_workers=3) as executor:
    # 1. 提交任务,拿到 Future 对象
    futures = [executor.submit(task, i) for i in [3, 1, 2]]
    
    # 2. as_completed 会在任务完成时立即产出,不按输入顺序
    for future in as_completed(futures):
        try:
            print(future.result()) 
        except Exception as e:
            print(f"出错了: {e}")
# 输出顺序:任务1 -> 任务2 -> 任务3 (按完成时间排序)

进阶:多线程同步与顺序控制

面试中常考:如何让 5 个线程按顺序输出 1, 2, 3, 4, 5...?

这需要用到条件变量 。我们不能让线程乱跑,必须给它们定好规矩:只有轮到自己的数字时才能打印,否则就等待。
代码实现:Condition 控制流

python 复制代码
import threading
from concurrent.futures import ThreadPoolExecutor

current_num = 1
max_num = 16
# 条件变量,自带一把锁
condition = threading.Condition()

def worker(thread_id):
    global current_num
    while True:
        with condition:  # 获取锁
            # 只要没结束,且当前数字不是该我打印的,我就等待
            # thread_id % 5 决定了该线程负责哪些数字 (1, 6, 11... 对应余数 1)
            while current_num <= max_num and current_num % 5 != (thread_id % 5):
                condition.wait() # 释放锁,挂起自己
            
            if current_num <= max_num:
                print(current_num, end=' ')
                current_num += 1
                condition.notify_all() # 唤醒所有人,让大家抢锁检查
            else:
                condition.notify_all() # 结束前唤醒,防止死锁
                return

with ThreadPoolExecutor(max_workers=5) as executor:
    for i in range(1, 6):
        executor.submit(worker, i)

面试高分话术:Python vs Java Spring

如果面试官问:"你在 Python 中用多线程,那在 Java Spring 中是怎么做的?有什么区别?"

在 Python 中,我主要使用 concurrent.futures.ThreadPoolExecutor 来处理 I/O 密集型任务,通过 map 或 submit 提交任务。

在 Java Spring 中,这个思想是相通的,但实现更加优雅:

  1. 声明式异步:Spring 提供了 @Async 注解,配合 ThreadPoolTaskExecutor,可以零侵入地实现异步调用,这比 Python 的手动提交更简洁。
  2. 无 GIL 限制:Java 没有 GIL,线程池可以真正利用多核 CPU 进行并行计算,这在处理 CPU 密集型任务时比 Python 多线程更有优势。
  3. 强大的编排:Java 的 CompletableFuture 提供了非常强大的链式异步编程能力,比 Python 原生的 Future 功能更丰富,适合处理复杂的微服务调用编排。
相关推荐
智算菩萨2 小时前
【论文复现】Applied Intelligence 2025:Auto-PU正例无标签学习的自动化实现与GPT-5.4辅助编程实战
论文阅读·python·gpt·学习·自动化·复现
老神在在0012 小时前
【Selenium 自动化精讲】浏览器弹窗与登录界面的本质区别 & 实操指南
javascript·学习·selenium·测试工具·自动化
小陈工3 小时前
2026年3月31日技术资讯洞察:AI智能体安全、异步编程突破与Python运行时演进
开发语言·jvm·数据库·人工智能·python·安全·oracle
·醉挽清风·3 小时前
学习笔记—Linux—信号阻塞&信号捕捉
linux·笔记·学习
Hello_Embed3 小时前
嵌入式上位机开发入门(四):TCP 编程 —— Client 端实现
网络·笔记·网络协议·tcp/ip·嵌入式
老李的勺子3 小时前
Agent 记忆失效的 5 种方式:完整排查复盘
python·llm
Leo655354 小时前
动态透视报表 + 查询接口 + Excel导出
开发语言·windows·python
清水白石0084 小时前
pytest Fixture 设计实战指南:作用域、依赖链、自动清理与测试资源高效复用
python·pytest
AnalogElectronic4 小时前
uniapp学习5,兼容微信小程序的俄罗斯方块游戏
学习·微信小程序·uni-app