V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
qixiangyangrm
V2EX  ›  Python

关于多线程和协程各自适用场景问题

  •  
  •   qixiangyangrm ·
    qixiangyang · 2019-12-05 20:27:35 +08:00 · 6335 次点击
    这是一个创建于 1596 天前的主题,其中的信息可能已经有所发展或是发生改变。
    协程和多线程都都可以通过异步提升效率。
    那么问题来了,哪些情况下应该用协程?哪些场景下用多线程?
    27 条回复    2019-12-07 13:59:36 +08:00
    lewis89
        1
    lewis89  
       2019-12-05 20:35:26 +08:00   ❤️ 1
    计算密集型使用多线程,由于 GIL 的存在 Python 只能用多进程+IPC 来同步 或者用其它的 python call Cworld 之类的线程黑魔法

    IO 密集型就随意了 多线程 协程问题应该都不大,因为绝大部分时间都在 IO 那里
    ClericPy
        2
    ClericPy  
       2019-12-05 20:37:36 +08:00
    可协程的都协程, 不可协程的丢到 executor 里做假协程

    就酱
    qixiangyangrm
        3
    qixiangyangrm  
    OP
       2019-12-05 20:50:42 +08:00
    @lewis89 @ClericPy 我个人是这样理解的,不知道对不对

    线程切换需要的资源消耗较多,如果启动大量线程且任务阻塞的时间较短,则多线程的效率就比较低。这时候应当避免使用多线程,应当考虑使用协程。
    当阻塞时间较长且 IO 请求的资源较大,则可以考虑使用多线程。
    PDX
        4
    PDX  
       2019-12-05 20:52:37 +08:00
    没有场景的问题,就看你喜欢用哪个。

    协程代码写异步和写同步一样,就这个区别。
    vkhsyj
        5
    vkhsyj  
       2019-12-05 21:05:17 +08:00
    应用能不用多线程就不用多线程
    ClericPy
        6
    ClericPy  
       2019-12-05 21:05:50 +08:00
    @qixiangyangrm #3 线程的好处是, 任何同步函数都能给你整异步来搞, 有些 C 库默认就是同步模型走不了协程, 也就没办法通过协程来提速, 但是线程套上依然可以搞 (以前遇到的问题就是 gevent 会被那种库 block 住)

    协程就像你说的, 在特别高并发场景下, 切换开销比线程低的多, 然而在当前大环境里面, 如果你整个业务系统都是在协程的主线程事件循环里跑的 (就像 uvicorn 托管个 asgi), 肯定有阻塞的函数都尽量用协程来实现, 无法实现的把它丢到多线程那个 executor 里造个类似 Future 的协程就兼容起来了, 开销影响也不会太大

    我试过在 Windows 上跑协程和多线程对比差距不大, 协程在 uvloop 尤其是 epoll 环境下面效率会不错, 可以对比 Benchmarker 项目里的一些结果, 然而真正提速的反而是依赖 C 的加成, 目前来说还是不用太纠结性能问题, 都用上 python 了, pure py 项目再优化天花板也有限的很

    就目前环境来说, 养成全局协程的习惯还是比较好的. 场景来说, Django 3.0 都正式把 asgi 搞起来了(比前个版本 channels 好像优化了不少), 多线程已经基本可以当做一个子集来用了
    conn4575
        7
    conn4575  
       2019-12-05 21:55:20 +08:00 via Android
    就 python 来说,多线程在协程面前没有任何优势,IO 多就协程,CPU 密集就多进程
    liuminghao233
        8
    liuminghao233  
       2019-12-05 22:25:53 +08:00 via iPhone
    @qixiangyangrm
    不是
    什么阻塞时间长 不是你用多线程的原因
    假设你有 100 万个 tcp 连接等数据 读阻塞
    这里有 100 万个 socket
    你难道开 100 万个线程?
    有 epoll 了不会这样玩的啊
    另外 io 操作你要不就用 callback
    要不就牺牲点性能用协程
    好处就是你的多个 io 操作的逻辑可以在同一个栈内解决
    不然你就只能 callback1–>callback2->...->callbackn 一个一个回调这样
    petelin
        9
    petelin  
       2019-12-06 00:21:45 +08:00 via iPhone
    这个问题非常简单
    xiecheng 是在线程之上的
    提供的非常高级的工具

    除非性能 功能满足不了你 否则不要用线程

    go 里面一个线程你都碰不到 不照样好好的吗
    wuwukai007
        10
    wuwukai007  
       2019-12-06 08:11:22 +08:00 via Android
    没用过协程,有个疑惑,协程如果是单线程的话,如果协成池其中一个任务挂了,是不是整个任务就挂了
    lewinlan
        11
    lewinlan  
       2019-12-06 09:57:39 +08:00
    去看操作系统的书,了解一下线程、调度器、systemcall 之类的东西,就能区分线程和协程了。

    协程是在用户态内实现可控调度的手段,线程是在内核态上不可控的调度。
    lewis89
        12
    lewis89  
       2019-12-06 10:33:22 +08:00
    @wuwukai007 #10 协程不是单线程,协程是在用户态加了一层透明调度的系统,像 Golang 这些的实现 就是将 你所有协程里面的同步调用都收集起来 遇到同步调用 全部给你收集到一个集合里面 然后用 Linux epoll 红黑树的实现,哪个协程的同步调用在 epoll 里面返回了 然后就让你这个协程到线程上面跑一跑 实际上就是在用户态 加了一层 IO 调度管理 谁调用同步 IO 了 就让谁去等着 把线程空出来 让其它没调用 IO 正等待 CPU 计算的协程跑起来 如果你是计算密集型的多线程任务 那完全没必要使用协程 计算密集型用协程几乎就是脱了裤子放屁。
    协程的好处其实就是开销比线程小一点,然后对于应用来讲,由于不用使用系统调用,在语言层面的托管系统就能解决 IO 密集型的调度问题。

    最后用户态的协程就有一个问题,操作系统没法对你进行内存管理,传统的多线程应用程序 内存管理实际上是托管给系统,系统采取多级缓存速度 从 cpu 的缓存 到内存缓存 到硬盘缓存 ,中间会根据调度来决定内存的操作管理。
    lewis89
        13
    lewis89  
       2019-12-06 10:34:49 +08:00
    @wuwukai007 #10 这样的话 好处非常明显 就是 一个进程里面 只要有一个线程 负责跟系统进行 IO 交互,其余的线程都可以根据协程任务的需要 讲线程分配给各个协程让他们先跑起来。
    altboy
        14
    altboy  
       2019-12-06 10:49:50 +08:00
    @lewis89 计算密集型使用多线程?不知道是打错了还是?
    lewis89
        15
    lewis89  
       2019-12-06 10:54:55 +08:00
    @altboy #14 难道不对吗? 协程目前的优势就是多了一层 IO 调度管理,在用户态将所有的 IO 调用 用一个线程就管理完了,如果是计算密集型 多个任务 使用多线程 没有毛病啊,而且内存也能由系统管理 随着任务规模增长 操作系统内存管理的优势就出来了
    qixiangyangrm
        16
    qixiangyangrm  
    OP
       2019-12-06 12:10:41 +08:00
    @altboy @lewis89 @lewinlan @petelin @liuminghao233 @conn4575 @ClericPy 受教了
    我不是计算机专业的,所以关于内核方面的知识掌握的比较少,以后还是要加强学习。
    个人感受现在用协程好像是大势所趋了,以后在日常的开发中,还是多使用协程。

    再次感谢各位的耐心回复。
    sylvos
        17
    sylvos  
       2019-12-06 14:20:59 +08:00 via iPhone
    @ClericPy 请教下大神,mysqlclient 怎么套上线程搞成异步,在 fastapi 异步函数上用,pymysql 效率不高,谢谢
    zunceng
        18
    zunceng  
       2019-12-06 15:29:09 +08:00
    go 和 java 都好说语言和框架把你限制的死了
    c++就比较惨 没有最佳实践 看你怎么实现

    当年用 boost asio 里一个 coro 写过协程 场景是一个私有协议的网络编程
    ClericPy
        19
    ClericPy  
       2019-12-06 15:40:28 +08:00
    @sylvos #17
    mysql client 支持协程的现在都烂大街了吧, 已经不是早年间给同步库套个线程做成协程就拿来用的时候了, 如果你非要, 看看 run_in_executor 的文档一看就懂了, 就是让一个跑在 concurrent.futures 的 ThreadPoolExecutor 里面的 Future 转成一个可以 await 的玩意. 我平时更喜欢用 asyncio 的 Future 去 await 一些协程玩意

    换个现成的轮子算了

    aio-libs/aiomysql
    encode/databases

    我平时用的前者, 但是更喜欢后者(很像 R 神那个 record), 这俩组织出的 aio 库太多太好使了, 至于速度... 没做过 Benchmark 不知道谁快, 看源码谁用 C 谁快吧

    httpx 没有 C 加成提速, 暂时只能用 aiohttp 代替 Requests, 但 starlette 是真的好使
    sylvos
        20
    sylvos  
       2019-12-06 16:14:27 +08:00 via iPhone
    @ClericPy 前辈,你现在还用到的轮子有哪些,能否分享一下?
    sylvos
        21
    sylvos  
       2019-12-06 16:26:10 +08:00 via iPhone
    @ClericPy 我现在用的 hug 做 api,切换到 fastapi,有必要吗,另外弱弱的问下 huey,redis,pandas,在异步函数里面用有问题吗
    ClericPy
        22
    ClericPy  
       2019-12-06 16:38:41 +08:00
    @sylvos #21
    可以看看 Benchmarker 那个项目, hug 从 falcon 包装来的, C 加成已经飞起来了, falcon 的性能接近 golang, fastapi 基于 starlette, 后者和 golang 差距还不小, 不过 fastapi 的几个设计思想挺有意思可以借鉴, 自带 swagger, 类型面向对象的类型检查是卖点, 不过这些都可以用插件代替
    后面三个东西... 看看有没有官方 asyncio 支持吧, 如果没有就丢进 executor 问题不大的, 通过多线程包出来的协程, 不是特别高并发的话开销可以忽略, 毕竟很早期(忘了是不是 aiomysql 了) 挺多轮子这么包的

    轮子什么的, 之前看的时候只是记个大概, 脑子里根据需求建个索引就没继续搞了, encode 和 aiolibs 是比较有名气的协程库组织, Django 当年提了 asgi 以后 uvicorn 火了一把

    我不是什么大神前辈, 就是个刷题找工作的可怜虫...
    wangyzj
        23
    wangyzj  
       2019-12-06 16:47:55 +08:00
    farverfull
        24
    farverfull  
       2019-12-06 16:59:50 +08:00 via Android
    基本不用多线程,cpu 密集型用多进程,io 量大异步,量小随意。项目能用就行。
    aaronhua
        25
    aaronhua  
       2019-12-07 10:10:52 +08:00
    @farverfull +1 基本用多线程,量小真的随意。
    reus
        26
    reus  
       2019-12-07 13:52:10 +08:00
    @petelin goroutine 的语义和 pthread 线程并没有区别,如果说用 goroutine 就是“不碰线程”,那用 pthread 的一样是“不碰线程”。goroutine 和 pthread 都是一个抽象层,只不过一个主流实现实现成 M:N 映射,一个主流实现成 1:1 映射。pthread 实际上也有 M:N 映射的实现。
    reus
        27
    reus  
       2019-12-07 13:59:36 +08:00
    @lewis89 错漏百出。google go 的 goroutine 实现并不是协程,现在已经有了基于信号的抢占式调度,之前也有在函数入口处的半抢占调度,不是协作式调度,自然不是协程。调度的时机,程序员是不可控制的,在纯计算的循环里也可能调度。这种性质,不是协程具备的。
    “然后对于应用来讲,由于不用使用系统调用“,大错,不存在不使用系统调用的程序。
    “最后用户态的协程就有一个问题,操作系统没法对你进行内存管理,传统的多线程应用程序 内存管理实际上是托管给系统”,大错,不论什么程序,操作系统都只负责内存的映射,具体怎么用是用户程序自己决定,这个和线程没有丝毫关系。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2805 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 31ms · UTC 12:06 · PVG 20:06 · LAX 05:06 · JFK 08:06
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.