当前位置:首页>python>【跟着AI学Python】Python进阶:并发与并行

【跟着AI学Python】Python进阶:并发与并行

  • 2026-09-08 21:09:47
【跟着AI学Python】Python进阶:并发与并行
【Qwen3-Max】

太棒了!欢迎来到 【跟着AI学Python】Python进阶:并发与并行!🎉今天我们将深入 Python 的三大并发模型:

💡 线程(Threading) → 轻量级,适合 I/O 密集型任务💡 进程(Multiprocessing) → 真正并行,适合 CPU 密集型任务💡 协程(Asyncio) → 单线程高并发,适合大量 I/O 操作(如网络请求)


🎯 今日目标

✅ 理解 GIL(全局解释器锁) 对线程的影响✅ 掌握 threading 模拟多用户并发访问✅ 使用 asyncio 实现非阻塞网络请求✅ 区分 并发 vs 并行


📘 一、核心概念速览

模型
模块
适用场景
是否绕过 GIL
内存共享
线程threading
I/O 密集(文件、网络)
❌ 受 GIL 限制
✅ 共享内存
进程multiprocessing
CPU 密集(计算、加密)
✅ 真正并行
❌ 独立内存
协程asyncio
高并发 I/O(Web API、爬虫)
❌ 单线程
✅ 共享内存

🔑 GIL 是什么?CPython 的全局锁,确保同一时刻只有一个线程执行 Python 字节码 → 线程无法真正并行 CPU 计算


🔧 二、实践 1:用线程模拟多用户访问网站

场景:10 个用户同时登录银行系统

# concurrent_demo.pyimport threadingimport timeimport random# 模拟银行登录接口(I/O 操作:网络延迟)def login_user(user_id):    print(f"👤 用户 {user_id} 开始登录...")    # 模拟网络延迟(0.5~2秒)    time.sleep(random.uniform(0.5, 2.0))    print(f"✅ 用户 {user_id} 登录成功!")# 同步方式(顺序执行)def sync_login():    start = time.time()    for i in range(1, 6):        login_user(i)    print(f"⏱️  同步耗时: {time.time() - start:.2f} 秒\n")# 多线程方式(并发执行)def thread_login():    start = time.time()    threads = []    for i in range(1, 6):        # 创建线程        t = threading.Thread(target=login_user, args=(i,))        threads.append(t)        t.start()  # 启动线程    # 等待所有线程完成    for t in threads:        t.join()    print(f"⏱️  多线程耗时: {time.time() - start:.2f} 秒\n")if __name__ == "__main__":    print("=== 同步登录 ===")    sync_login()    print("=== 多线程登录 ===")    thread_login()

▶️ 输出示例

✅ 为什么快?虽然受 GIL 限制,但 I/O 等待期间会释放 GIL,其他线程可运行!


⚠️ 线程安全问题(重要!)

如果多个线程操作共享资源(如全局变量),需加锁:

# 不安全示例counter = 0def unsafe_increment():    global counter    for _ in range(100000):        counter += 1  # 非原子操作!# 安全示例(使用 Lock)lock = threading.Lock()def safe_increment():    global counter    for _ in range(100000):        with lock:  # 自动 acquire/release            counter += 1

🔒 银行系统启示:多用户同时取款时,必须用锁保护余额更新!


🔧 三、实践 2:用 asyncio 实现非阻塞网络请求

场景:同时请求多个 API(比线程更高效!)

# async_demo.pyimport asyncioimport aiohttp  # 需安装: pip install aiohttpimport time# 异步函数:获取网页内容async def fetch_url(session, url):    print(f"📡 请求: {url}")    async with session.get(url) as response:        text = await response.text()        print(f"✅ 收到响应: {url} (长度: {len(text)})")        return len(text)# 主协程:并发请求多个 URLasync def main_async():    urls = [        "https://baijiahao.baidu.com/s?id=1859560634159209643",        "https://baijiahao.baidu.com/s?id=1859560634159209643",        "https://baijiahao.baidu.com/s?id=1859560634159209643"    ]    async with aiohttp.ClientSession() as session:        # 并发执行所有请求        tasks = [fetch_url(session, url) for url in urls]        results = await asyncio.gather(*tasks)        return results# 同步对比(使用 requests)import requestsdef main_sync():    urls = ["https://baijiahao.baidu.com/s?id=1859560634159209643"] * 3    results = []    for url in urls:        print(f"📡 请求: {url}")        resp = requests.get(url)        print(f"✅ 收到响应: {url}")        results.append(len(resp.text))    return resultsif __name__ == "__main__":    # 测试异步    start = time.time()    asyncio.run(main_async())    print(f"⏱️  Asyncio 耗时: {time.time() - start:.2f} 秒\n")    # 测试同步    start = time.time()    main_sync()    print(f"⏱️  Requests 耗时: {time.time() - start:.2f} 秒")

▶️ 输出示例

✅ asyncio 优势:

  • 单线程处理数千并发连接
  • 无上下文切换开销(比线程轻量)
  • 适合 I/O 密集型高并发场景(如 Web 服务器)

🔍 四、关键对比:何时用哪种?

场景
推荐方案
下载 100 个网页
✅ asyncio(最快)
处理 10 个用户上传文件
✅ threading(简单)
计算 100 万次素数
✅ multiprocessing(绕过 GIL)
实时聊天服务器
✅ asyncio(高并发)
GUI 应用后台任务
✅ threading(避免卡界面)

🧪 五、结合银行系统:并发取款模拟

# 模拟多用户同时取款(带锁保护)import threading, timeclass BankAccount:    def __init__(self, balance=1000):        self.balance = balance        self.lock = threading.Lock()  # 线程锁    def withdraw(self, amount, user):        with self.lock:  # 关键:保护共享资源            if self.balance >= amount:                print(f"👤 {user} 取款 {amount}")                self.balance -= amount                time.sleep(0.1)  # 模拟处理时间                print(f"✅ {user} 成功,余额: {self.balance}")            else:                print(f"❌ {user} 余额不足!")# 测试account = BankAccount(500)threads = []for i in range(3):    t = threading.Thread(target=account.withdraw,                          args=(200, f"用户{i+1}"))    threads.append(t)    t.start()for t in threads:    t.join()

🔒 没有锁的后果:可能出现“超取”(余额变成负数)或数据不一致!


📝 小结:并发编程心法

I/O 密集 → 用线程或 asyncioCPU 密集 → 用多进程高并发网络 → 首选 asyncio共享状态 → 必须加锁!

你的程序将因此:

  • ✅ 响应更快(用户不等待)
  • ✅ 资源利用率更高(CPU/网络满载)
  • ✅ 架构更现代(支持微服务、实时系统)

🎉 恭喜完成并发与并行进阶!你已掌握让 Python 突破单线程限制的核心能力!

继续加油,你的代码正在变得越来越强大!⚡

最新文章

随机文章