【Python】进程、线程、协程、生成器

1. 进程、线程、协程对比

类型 开销 并发方式 适用场景 GIL
协程 极小 单线程内切换 超高 IO 并发、爬虫、接口 不影响
线程 线程切换 普通 IO 并发 受 GIL
进程 多核并行 CPU 密集计算 不受 GIL

2. Python 进程

(1)进程的特点

  • 内存完全隔离

  • 不受GIL锁限制:

    • GIL锁:本质上是个互斥锁。它确保同一时刻只有一个线程执行Python字节码,是CPython解释器为保证线程安全而设计的互斥机制。
  • 开销比创建线程和协程大。

  • 独立的PID

(2)进程的使用场景

  • 【推荐场景】

    • CPU 密集型计算(最常用)

      • 大规模数值运算、矩阵运算
      • 数据分析、数据清洗大批量数据
      • 加密解密、哈希爆破、暴力运算
      • 深度学习数据预处理、批量特征计算
    • 大批量并发独立任务

      • 批量爬取大量站点(高并发爬取)
      • 批量压缩 / 解压文件
      • 批量图片处理:裁剪、水印、转格式
      • 批量音视频转码、剪辑
    • 任务隔离,崩溃互补影响

      • 子任务容易报错崩溃,不想主程序挂掉
      • 独立业务模块拆分,一个进程崩不影响整体
      • 沙箱执行用户代码、执行未知脚本
    • 突破单进程内存 / 资源限制

      • 单个任务占用内存极大,拆分多进程分担内存
      • 限制单进程 CPU 使用率,拆分负载
  • 【不推荐场景】

    • 需要频繁共享大量数据。
    • 任务执行速度极快,频次极高:进程的创建和销毁远高于线程和携程。
    • IO密集型(网络请求,文件读写,接口请求)

(3)代码示例

  • 简单创建
import multiprocessing
import time

def run():
    print("我是子进程")
    time.sleep(2)

if __name__ == '__main__':
    # 创建子进程
    p = multiprocessing.Process(target=run)
    p.start()   # 启动
    p.join()    # 等待结束
  • 进程池

    • map批量统一执行
    import time
    from multiprocessing import Pool
    
    def work(x):
        time.sleep(1)
        return x * 2
    
    if __name__ == '__main__':
        # 开启4个进程
        with Pool(4) as p:
            # 自动分配任务
            res = p.map(work, [1,2,3,4,5,6])
        print(res)
    
    • apply_async 异步提交(最常用):不阻塞,还可以拿结果
    import time
    from multiprocessing import Pool
    
    def task(num):
        time.sleep(1)
        print(f"执行任务{num}")
        return num
    
    if __name__ == '__main__':
        pool = Pool(3)  # 最大3个进程
        
        result_list = []
        for i in range(6):
            # 提交任务
            ret = pool.apply_async(task, args=(i,))
            result_list.append(ret)
        
        # 关闭进程池,不再接收新任务
        pool.close()
        # 等待所有任务完成
        pool.join()
    
        # 取出所有结果
        for r in result_list:
            print(r.get())
    
    • 回调函数
    from multiprocessing import Pool
    
    def func(n):
        return n**2
    
    def callback(res):
        print("结果:",res)
    
    if __name__ == '__main__':
        with Pool(2) as p:
            p.apply_async(func,args=(5,),callback=callback)
            p.join()
    
    • 进程池 + 队列
    from multiprocessing import Pool,Queue
    
    def worker(q):
        while not q.empty():
            data = q.get()
            print("处理:",data)
    
    if __name__ == '__main__':
        q = Queue()
        # 存入任务
        for i in range(10):
            q.put(i)
        
        pool = Pool(3)
        for _ in range(3):
            pool.apply_async(worker,args=(q,))
        
        pool.close()
        pool.join()
    

3. Python 线程

(1)线程的特点

  • 资源共享
  • 开销极小
  • 受GIL全局解释器锁限制

(2)线程的场景

  • 【推荐场景】

    • IO 密集型任务(最核心)

      • 网络请求:爬虫、接口调用、HTTP 请求
      • 文件操作:批量读写本地文件
      • 数据库查询、Redis 读写
      • 串口、网络连接等待
      • 【特点】:大部分时间在等待阻塞,CPU 空闲,线程切换开销极小
    • 大量轻量并发任务

      • 批量发送消息、短信、邮件
      • 批量访问接口、批量校验数据
      • 定时批量小任务
    • 需要共享大量数据

      • 线程共享同一块内存空间
      • 全局变量、列表字典直接共用
      • 通信简单、不用管道 / 队列,开发简单
    • 任务执行快、启停频繁

      • 线程创建销毁开销远小于进程
      • 短时小任务优先线程
    • 界面程序后台常驻任务

      • GUI 软件后台刷新、监听、轮询
      • 不卡死主界面
  • 【不推荐场景】

    • CPU 密集型计算:大数据运算、循环暴力计算、矩阵运算、加密解密;GIL 限制同一时刻只有一个线程跑,多线程反而变慢
    • 需要利用多核 CPU 跑满性能:线程做不到。

(3)代码示例

  • 简单创建
import threading
import time

def job():
    print("子线程开始运行")
    time.sleep(2)
    print("子线程结束")

if __name__ == "__main__":
    # 创建线程
    t = threading.Thread(target=job)
    # 启动
    t.start()
    # 等待线程结束
    t.join()
    print("主线程走完")
  • 线程池

    • 基础用法
    import time
    from concurrent.futures import ThreadPoolExecutor
    
    def task(n):
        time.sleep(1)
        print(f"执行任务{n}")
        return n*2
    
    if __name__ == '__main__':
        # 最大并发5个线程
        with ThreadPoolExecutor(max_workers=5) as t:
            for i in range(10):
                t.submit(task, i)
    
    • 获取任务返回结果
    from concurrent.futures import ThreadPoolExecutor
    
    def func(x):
        return x+10
    
    if __name__ == '__main__':
        with ThreadPoolExecutor(3) as pool:
            res = [pool.submit(func,i) for i in range(5)]
            for r in res:
                print(r.result()) # 拿返回值
    
    • map批量传参
    from concurrent.futures import ThreadPoolExecutor
    
    def work(x):
        return x**2
    
    if __name__ == '__main__':
        with ThreadPoolExecutor(4) as t:
            ret = t.map(work, [1,2,3,4,5])
        print(list(ret))
    
    • 线程池 + 队列
    from concurrent.futures import ThreadPoolExecutor
    from queue import Queue
    import time
    
    q = Queue()
    # 放入任务
    for i in range(20):
        q.put(i)
    
    def handle():
        while not q.empty():
            num = q.get()
            time.sleep(0.5)
            print(f"处理:{num}")
            q.task_done()
    
    if __name__ == '__main__':
        with ThreadPoolExecutor(5) as pool:
            for _ in range(5):
                pool.submit(handle)
    

4. 协程

(1)什么是协程

微线程,用户态轻量级并发,单线程内切换执行

  • 不用开线程、不开进程,一条主线程里来回切换任务
  • 开销极小,几万、几十万协程轻松跑
  • 自带自动切换:遇到 IO 阻塞就切走,不卡 CPU

(2)核心特点

  • 极致轻量:创建成本远低于线程
  • 单线程并发:不占用多核心,只利用等待时间
  • 无锁竞争:全程单线程,不用互斥锁
  • 只适合 IO 阻塞:网络请求、爬虫、读写、延时
  • CPU 密集无效:纯计算用协程反而变慢

(3)协程的场景

  • 【推荐场景】

    • 超高并发 IO 密集(最主流)

      • 海量爬虫、批量请求接口
      • 大量 HTTP/HTTPS 异步请求
      • 批量调用第三方 API、微服务接口
      • 异步数据库、Redis、MQ 读写
    • 大量延时等待任务

      • 批量定时任务、延时执行
      • 轮询监听、长连接心跳
      • 大量休眠等待类业务
    • 高并发服务端开发

      • 异步 Web 框架:FastAPI、Sanic、Tornado
      • 搭建高吞吐接口服务、网关服务
      • 后端推送、消息推送服务
    • 轻量海量并发任务

      • 上万级别的轻量 IO 任务,线程池会卡线程上限,协程轻松顶住
    • 异步文件 / 网络读写

      • 异步读写文件、异步 TCP/UDP 通信
  • 【不推荐场景】

    • CPU 密集计算:协程单线程串行执行,计算任务会直接卡死整个事件循环,越跑越慢
    • 同步阻塞代码混用:大量time.sleep()、同步请求、同步数据库、会阻塞整个协程池
    • 需要利用多核并行计算:协程天生单线程,要多核必须协程 + 多进程组合

(4)代码示例

  • 基础用法
import asyncio

# 定义协程函数
async def say_hello(name):
    print(f"开始 {name}")
    # 模拟IO等待,自动切换任务
    await asyncio.sleep(2)
    print(f"结束 {name}")

async def main():
    # 并发运行多个协程
    task1 = asyncio.create_task(say_hello("任务1"))
    task2 = asyncio.create_task(say_hello("任务2"))
    await task1
    await task2

# 运行协程程序
asyncio.run(main())
  • 并发使用

    • gather批量并发(最常用)
    import asyncio
    
    async def work(num):
        await asyncio.sleep(1)
        print(f"完成任务{num}")
    
    async def main():
        tasks = [work(i) for i in range(5)]
        await asyncio.gather(*tasks)
    
    asyncio.run(main())
    
    • 顺序执行(不用协程优势)
    async def main():
        await work(1)
        await work(2)
    
    • 异步请求实战(爬虫标配)
    import aiohttp
    import asyncio
    
    async def get_url(url):
        async with aiohttp.ClientSession() as session:
            async with session.get(url) as res:
                print(await res.text())
    
    async def main():
        urls = ["https://www.***.com"]*3
        tasks = [get_url(u) for u in urls]
        await asyncio.gather(*tasks)
    
    asyncio.run(main())
    

5. 生成器

(1)什么是生成器?

一边循环,一边生成数据,不一次性存全部数据。

(2)生成器的特点

  • 惰性执行:(懒加载):不会一次性生成所有数据,用一个生成一个,不用不生成
  • 极度省内存:不存放全部元素,值存生成规则,海量数据也不爆内存。
  • 单向不可逆:只能从前向后遍历,遍历完就空了,不能回头重读
  • 暂停 / 恢复执行:yield 遇到就暂停,下次迭代从暂停位置继续执行。
  • 无法索引 / 切片:不能用 gen[0]取值,没有下标,不是列表。
  • 一次性消费:遍历一次结束,再次遍历拿不到任何数据。

(3)生成器的场景

  • 【推荐场景】

    • 读取超大文件
    • 遍历海量数据 / 数据库分页查询
    • 生成无限序列
    • 爬虫分批请求数据
    • 数据过滤,清晰,流式处理。
    • 协程 / 任务调度(yield实现简单协程)
    • 迭代器嵌套,多层数据拆解。
    • 大数据运算,数据推演
  • 【不推荐场景】

    • 需要重复多次遍历同一组数据
    • 需要随机访问数据(下标取值)

(4)代码示例:

  • 基础示例
# 定义生成器函数
def my_generator():
    yield 1  # 第一次调用返回1,暂停在这里
    yield 2  # 第二次调用返回2,暂停在这里
    yield 3  # 第三次调用返回3

# 创建生成器对象
gen = my_generator()
print(gen)  # <generator object my_generator at 0x...>

# 取值
print(next(gen))  # 1
print(next(gen))  # 2
print(next(gen))  # 3
  • 进阶示例
# 生成 1~n 的平方数
def square_gen(n):
    for i in range(1, n+1):
        yield i ** 2  # 每次循环暂停,返回平方值

# 使用
gen = square_gen(3)
for num in gen:
    print(num)  # 依次输出 1,4,9
posted @ 2026-05-16 20:03  静心笃行。  阅读(18)  评论(0)    收藏  举报