Python并发编程深度解析(threading/multiprocessing/asyncio事件循环/async-await/Future/aiohttp·面试真题)

Python 并发要先分清多线程、多进程和异步 IO 的适用场景,选型错误往往比语法错误更影响性能和稳定性。

并发编程是Python进阶核心分水岭,是爬虫、接口服务、异步任务、高并发项目的必备能力。Python提供多线程、多进程、异步IO三套并发方案,三者适用场景、底层原理、优缺点差异极大,绝大多数新手的并发报错、效率低下、卡死问题,均是选型错误导致。

本文全覆盖指定重难点:threading多线程、multiprocessing多进程、asyncio事件循环、async/await异步语法、Future未来对象、aiohttp异步请求。全程搭配可落地实战代码、底层原理拆解、工程高频坑点、大厂面试真题,一站式吃透Python全场景并发体系。

一、并发核心基础认知

1.1 并发与并行区别

并发:单核CPU快速切换多个任务,宏观同时执行,微观交替执行,解决IO阻塞等待问题;

并行:多核CPU同时执行多个任务,真正意义上的同时运行,解决CPU密集计算问题。

1.2 Python三大并发方案选型

1、多线程 threading:适配IO密集任务,受GIL锁限制,无法利用多核,线程开销极小;

2、多进程 multiprocessing:适配CPU密集任务,突破GIL限制,利用多核CPU,进程开销大;

3、异步IO asyncio:超高IO并发场景,单线程协程,无线程切换开销,并发量级远超多线程。

1.3 核心关键:GIL全局解释器锁

GIL是Python经典核心痛点:同一时刻一个进程内只有一个线程执行Python代码

后果:多线程无法实现CPU并行,仅适合IO阻塞场景;CPU密集任务必须用多进程。

二、多线程 threading(IO密集首选)

线程是进程内的最小执行单元,共享进程资源,创建销毁开销极低,适合文件读写、网络请求、接口等待等IO阻塞型任务

2.1 多线程基础实战

import threading
import time

def task(name, delay):
    print(f"线程 {name} 启动,等待 {delay}s")
    time.sleep(delay)  # 模拟IO阻塞
    print(f"线程 {name} 执行完成")

if __name__ == "__main__":
    t1 = threading.Thread(target=task, args=("线程1", 2))
    t2 = threading.Thread(target=task, args=("线程2", 1))

    t1.start()
    t2.start()

    # 阻塞主线程,等待子线程执行完毕
    t1.join()
    t2.join()
    print("所有线程执行完毕")

2.2 守护线程

设置 daemon=True 开启守护线程,主线程退出时,守护线程会强制终止,无需等待执行完毕,适合后台轮询、日志监控等附属任务。

import threading
import time

def daemon_task():
    while True:
        print("守护线程运行中...")
        time.sleep(1)

if __name__ == "__main__":
    t = threading.Thread(target=daemon_task, daemon=True)
    t.start()
    time.sleep(3)
    print("主线程退出,守护线程随之终止")

2.3 线程安全与锁机制(高频坑点)

多线程共享全局变量,并发修改会出现数据错乱,需通过线程锁保证数据一致性。

import threading

num = 0
lock = threading.Lock()

def add_count():
    global num
    for _ in range(100000):
        lock.acquire()  # 加锁
        num += 1
        lock.release()  # 解锁

if __name__ == "__main__":
    t1 = threading.Thread(target=add_count)
    t2 = threading.Thread(target=add_count)
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print("最终计数:", num)  # 正确结果200000

三、多进程 multiprocessing(CPU密集首选)

多进程会创建多个独立进程,每个进程拥有独立GIL锁,可充分利用多核CPU,彻底突破多线程的性能瓶颈,适合数值计算、数据分析、模型推理等CPU密集任务。

核心特点:进程间资源完全隔离、无共享变量、开销大于线程、支持多核并行。

3.1 多进程基础实战

import multiprocessing
import time

def cpu_task(num):
    # 模拟CPU密集计算
    res = 0
    for i in range(num):
        res += i ** 2
    print(f"进程 {multiprocessing.current_process().name} 计算完成")

if __name__ == "__main__":
    p1 = multiprocessing.Process(target=cpu_task, args=(1000000,))
    p2 = multiprocessing.Process(target=cpu_task, args=(1000000,))

    p1.start()
    p2.start()
    p1.join()
    p2.join()
    print("所有进程执行完毕")

3.2 进程间通信(Queue队列)

进程资源隔离,无法直接共享变量,通过Queue队列实现安全数据交互。

import multiprocessing

def send_msg(q):
    q.put("进程数据通信测试")

def recv_msg(q):
    print("接收数据:", q.get())

if __name__ == "__main__":
    q = multiprocessing.Queue()
    p1 = multiprocessing.Process(target=send_msg, args=(q,))
    p2 = multiprocessing.Process(target=recv_msg, args=(q,))
    p1.start()
    p2.start()

3.3 进程池(工程高频)

频繁创建销毁进程开销极大,进程池可复用进程资源,控制并发数量,适合批量任务处理。

import multiprocessing

def pool_task(x):
    return x * x

if __name__ == "__main__":
    # 开启4个进程的进程池
    with multiprocessing.Pool(processes=4) as pool:
        res = pool.map(pool_task, [1,2,3,4,5])
    print("批量计算结果:", res)

四、asyncio异步IO核心体系(高并发IO终极方案)

asyncio是Python原生异步编程框架,基于事件循环实现单线程协程并发,无线程/进程切换开销,IO并发量级远超多线程、多进程,是爬虫、异步接口、高并发服务的核心方案。

4.1 核心概念:事件循环

**事件循环(Event Loop)**是异步程序的调度核心,负责监听、调度、执行所有协程任务,自动切换阻塞任务,实现异步并发。

执行逻辑:循环检测所有任务 → 就绪任务立即执行 → 阻塞任务挂起 → 等待IO完成后唤醒继续执行。

4.2 async/await 异步语法

1、async:修饰普通函数为协程函数,调用后不执行,仅生成协程对象;

2、await:阻塞等待异步任务完成,主动让出CPU权限,让事件循环执行其他任务。

核心规则:await 只能写在 async 函数内部。

4.3 基础异步实战

import asyncio

# 定义协程函数
async def async_task(name, delay):
    print(f"异步任务 {name} 启动,等待 {delay}s")
    await asyncio.sleep(delay)  # 异步阻塞,不占用CPU
    print(f"异步任务 {name} 执行完成")

async def main():
    # 创建多个协程任务
    task1 = asyncio.create_task(async_task("A", 2))
    task2 = asyncio.create_task(async_task("B", 1))
    # 等待所有任务执行完毕
    await task1
    await task2

if __name__ == "__main__":
    # 启动事件循环,运行主协程
    asyncio.run(main())

五、Future未来对象(异步底层核心)

Future 是 asyncio 异步编程的底层基础对象,代表一个未来即将完成的任务结果,协程Task是Future的子类。

核心作用:存储异步任务的执行状态、返回结果、异常信息,实现任务结果的异步回调与等待。

5.1 Future核心实战

import asyncio

async def set_future_result(future):
    await asyncio.sleep(2)
    future.set_result("Future任务执行完成!")

async def main():
    # 创建Future对象
    future = asyncio.Future()
    # 启动任务为Future赋值
    await asyncio.create_task(set_future_result(future))
    # 等待Future获取结果
    result = await future
    print(result)

if __name__ == "__main__":
    asyncio.run(main())

5.2 Future与Task区别

1、Future:底层抽象对象,仅负责存储结果、状态,无执行逻辑;

2、Task:Future子类,用于包装协程函数,具备调度执行能力,是实际运行的异步任务。

六、aiohttp异步网络请求(工程高频)

Python原生不支持异步HTTP请求,aiohttp是专门为asyncio设计的异步网络请求库,替代同步requests库,可实现超高并发爬虫、异步接口调用,并发效率远超多线程爬虫。

6.1 环境安装

# 安装异步请求库
# pip install aiohttp

6.2 aiohttp高并发请求实战

import aiohttp
import asyncio

# 异步请求函数
async def fetch(session, url):
    async with session.get(url) as resp:
        status = resp.status
        text = await resp.text()
        print(f"请求地址:{url},状态码:{status}")
        return text

async def main():
    # 创建异步会话(全局复用,性能更高)
    async with aiohttp.ClientSession() as session:
        url_list = [
            "https://www.baidu.com",
            "https://www.bing.com",
            "https://www.csdn.net"
        ]
        # 批量创建异步任务
        tasks = [fetch(session, url) for url in url_list]
        # 并发执行所有任务
        await asyncio.gather(*tasks)

if __name__ == "__main__":
    asyncio.run(main())

6.3 aiohttp核心规范

1、必须使用 ClientSession 发起请求,禁止频繁创建销毁会话;

2、所有请求、响应读取操作必须加 await;

3、异步IO场景优先aiohttp,同步场景使用requests,禁止混用。

七、三大并发方案选型总结(工程准则)

1、IO密集任务(网络请求、文件读写、数据库查询):优先 asyncio + aiohttp,超高并发;简单少量IO任务可用 threading 多线程;

2、CPU密集任务(计算、解析、建模):必须用 multiprocessing 多进程,突破GIL多核并行;

3、绝对禁止:CPU密集用多线程、IO密集用多进程(资源浪费、效率极低)。

八、高频工程坑点汇总

1、GIL误区:多线程不能多核并行,仅加速IO任务,CPU任务无效;

2、进程开销:进程创建销毁开销大,批量任务优先进程池复用;

3、异步阻塞坑:异步函数中禁止使用time.sleep、requests同步方法,会阻塞整个事件循环,必须用异步对应API;

4、Future空结果:未set_result的Future会永久阻塞,需做好异常捕获;

5、aiohttp会话复用:频繁创建ClientSession会造成端口耗尽,必须全局复用。

九、高频面试真题汇总

题目1:GIL全局解释器锁是什么?带来什么影响?

答案:GIL是CPython的全局锁,保证同一时刻一个进程内只有一个线程执行代码。导致Python多线程无法实现CPU多核并行,仅适合IO密集任务,CPU密集任务必须使用多进程。

题目2:线程、进程、协程的区别?

答案:线程轻量、共享资源、受GIL限制,适配IO密集;进程独立隔离、支持多核、开销大,适配CPU密集;协程单线程异步、无切换开销、并发量最高,适配超高IO并发。

题目3:asyncio事件循环的作用?async/await原理?

答案:事件循环是异步任务调度核心,负责统筹所有协程任务的执行、挂起、唤醒;async定义协程函数,await主动让出CPU,等待IO完成,实现单线程异步并发。

题目4:Future和Task的关系?

答案:Future是底层结果容器,存储任务状态和返回值,无执行能力;Task继承自Future,用于包装协程,具备任务调度执行能力,是异步任务的实际载体。

题目5:aiohttp和requests的区别?

答案:requests是同步请求,阻塞等待响应,并发效率低;aiohttp是异步请求,基于asyncio实现,非阻塞并发,适合高并发爬虫、异步服务,是异步网络请求标准方案。

十、全文终极背诵总结

1、多线程threading:轻量IO并发,共享资源,需线程锁,受GIL限制,不适合计算任务。

2、多进程multiprocessing:多核并行,突破GIL,资源隔离,开销大,专属CPU密集计算场景。

3、asyncio事件循环:异步调度核心,统筹所有协程任务,实现非阻塞高并发。

4、async/await:异步专属语法,async定义协程,await阻塞等待并让出CPU,是异步编程基础。

5、Future:异步底层结果容器,存储任务状态与结果,Task为其子类,负责任务执行调度。

6、aiohttp:异步HTTP请求库,替代同步requests,适配asyncio,实现超高并发网络请求。

7、核心选型:IO用异步/多线程,CPU用多进程,严格区分场景,规避性能瓶颈。

后浪云移动端信息流广告 后浪云主机服务 适合长期部署、独立站和海外机房需求。