Python中如果一个程序需要进行大量的 IO 操作,应当使用并行还是并发?

null
Python中如果一个程序需要进行大量的 IO 操作,应当使用并行还是并发?

37 回复

看具体业务


核心答案:用并发,特别是异步IO(asyncio)。

对于大量IO密集型任务,并发(特别是异步并发)是标准做法。并行(多进程)在这里是错误选择,原因很简单:

  1. IO密集型 vs CPU密集型

    • IO密集型:程序大部分时间在等待(网络请求、文件读写、数据库查询)。此时CPU是空闲的。
    • CPU密集型:程序大部分时间在进行计算(数据处理、图像渲染、复杂算法)。此时CPU是满的。
  2. 为什么不用并行(多进程)

    • 创建进程开销大,消耗更多内存。
    • 进程间切换成本高。
    • 最关键的是:当某个进程在等待IO时,它占用的CPU核心就闲着,但操作系统调度器仍然把它当作一个需要调度的任务,这造成了资源浪费。开10个进程等网络响应,可能10个都在等,白占资源。
  3. 为什么用并发(特别是异步)

    • 线程(并发):比进程轻量,但Python有GIL,对于纯IO任务,GIL影响不大,因为线程在IO时会释放GIL。但线程切换仍有开销,且编码需注意锁。
    • 异步IO(asyncio)这是最推荐的方式。它使用单线程,通过事件循环在多个IO任务间快速切换。当一个任务发起IO请求后,立即让出控制权,事件循环去执行其他就绪的任务。IO完成时,事件循环再回来处理结果。这避免了线程/进程的创建和切换开销,能轻松管理成千上万的并发连接。

代码示例:异步IO(asyncio)处理大量HTTP请求

import asyncio
import aiohttp  # 需要安装:pip install aiohttp

async def fetch_url(session, url):
    """异步获取单个URL的内容"""
    try:
        async with session.get(url, timeout=10) as response:
            # 这里可以处理响应,例如读取文本
            text = await response.text()
            return f"{url}: 成功,长度 {len(text)}"
    except Exception as e:
        return f"{url}: 失败 - {e}"

async def main(urls):
    """主函数,并发获取所有URL"""
    # 创建一个共享的aiohttp会话,比每个请求都创建会话更高效
    async with aiohttp.ClientSession() as session:
        # 为每个URL创建一个协程任务
        tasks = [fetch_url(session, url) for url in urls]
        # 并发运行所有任务,并等待它们全部完成
        results = await asyncio.gather(*tasks)
        # 输出结果
        for result in results:
            print(result)

if __name__ == "__main__":
    # 示例URL列表
    urls = [
        "https://httpbin.org/delay/1",  # 模拟延迟1秒的请求
        "https://httpbin.org/delay/2",
        "https://httpbin.org/delay/1",
        "https://api.github.com",
        "https://docs.python.org",
    ]
    
    # 运行事件循环
    asyncio.run(main(urls))

代码解释

  1. async def 定义异步函数(协程)。
  2. await 用于挂起当前协程,直到IO操作完成,期间事件循环可以执行其他任务。
  3. asyncio.gather() 并发运行多个协程,并收集它们的结果。
  4. aiohttp.ClientSession() 用于高效地发起HTTP请求。
  5. asyncio.run() 启动事件循环。

总结建议:IO密集型任务直接用asyncio。

并行 /并发 有什么区别?

并行:生产流水线
并发:购物订单

并行还是并发不看 IO,要看计算量大不大

揭秘 iPhone 售后的灰色产业链,是什么让苹果每年损失十多亿美元
https://zhuanlan.zhihu.com/p/46463952

「据外媒 The Information 统计,苹果在 2013 年用于全球售后维修的预算为 16 亿美元,但最终花了 37 亿美元来维持业务运营,而中国市场的售后处理占了业务支出的大头。」

楼上发错了 抱歉!

并行 /并发 是不是这么理解:
并行,多进程甚至多服务器
并发,单进程多线程

对于将来需要扩展的业务,显然必然选择多进程多服务器这种“分布式”,对于仅仅需要多几个线程充分用满当前 cpu,则并发多线程去运行

io 的瓶颈在于磁盘, 网卡等, 无法真正的并行, 即使调用上的并发量上去了, 效果也挺差的. 用异步的方式来解决, 降低消耗.

并行可类比为 2 个人同时去一台饮水机的两个出水口接水, 两个人一个要热水,一个要凉水, 并发类比两个人一起去都要热水

一张嘴同时说中午与英文是并行;说一句中午再说一句英文是并发。

这种咬文嚼字没有意义

erlang 解决烦劳

猜你实际上是想问密集 I/O 时 Python 用 多进程 or 多线程?

假若是问这个,两个都行啊。CPU 密集,则只得用多进程。

应当用 redis 等玩意

有兴趣可以看看这个,https://sites.google.com/site/1024cores/。 这个人设计了 golang scheduler (我估计 golang 现在任然是这个 scheduler ) https://docs.google.com/document/d/1TTj4T2JO42uD5ID9e89oa0sLKhJYD0Y_kqxDv3I3XMw/edit
我当时只是想看看 lockfree 的,可能你对 Scalable Architecture 这部分更感兴趣。
站在我的水平上,我的意见是并发能达到需求,就不考虑并行。越往底层走,越难 handle,当然也越有意思。

并发是指一次处理多件事。

并行是指一次做多件事。

二者不同,但是有联系。

一个关于结构,一个关于执行。

并发用于制定方案,用来解决可能(但未必)并行的问题。

– Rob Pike Go 语言的创造者之一

我自己想法是多线程+异步.

大量 io 最好可以通过异步流程,分离 io 部分和逻辑部分。因为整体 io 耗时就摆在那里。。逻辑部分可以做点进度条之类的东西让用户感觉良好就好。。

都可以

看情况

这两个冲突吗? 多进程+协程

看起来是 concurrency 和 parallel 的关系

我一直觉得并行是并发的一种方式

网络 IO 多开可能提速,硬盘 IO 能提速嘛?

反了吧

并发就是假的并行…
并行是指多核情况下同时处理多任务,并发是在单核下利用时间片来模拟同时处理多任务。

回答楼主问题。IO 密集型的程序使用多线程或线程并发即可,这种情况下能最大化利用 CPU。

这个应该使用并法逻辑流的 I/O 多路复用

这是啥问题?
并行和并发的定义是啥,不理解,能否补充一下英文关键词或者具体业务场景?
大量 IO 是指的磁盘 IO 还是网络 IO ?

异步加协程最好了

并发是场景,并行是方法,你怎么并发 IO ?

我做过, 多线程+mmap

看情况 io 上限摆在那里 极限情况下并行并不能超过物理上限
反而因为大量上下文切换降低性能

你试试 多个大量数据的 SQL 文件并行插入 结果插入速度远低于顺序执行这些 sql 文件


但是多个少量 sql 文件 并行插入性能就不错

所以 看情况

中间层缓冲,统一 IO 进磁盘,随机 IO 真的废

是的😁

回到顶部