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

2019-12-05 20:27:35 +08:00
 qixiangyangrm
协程和多线程都都可以通过异步提升效率。
那么问题来了,哪些情况下应该用协程?哪些场景下用多线程?
6352 次点击
所在节点    Python
27 条回复
lewis89
2019-12-05 20:35:26 +08:00
计算密集型使用多线程,由于 GIL 的存在 Python 只能用多进程+IPC 来同步 或者用其它的 python call Cworld 之类的线程黑魔法

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

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

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

协程代码写异步和写同步一样,就这个区别。
vkhsyj
2019-12-05 21:05:17 +08:00
应用能不用多线程就不用多线程
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
2019-12-05 21:55:20 +08:00
就 python 来说,多线程在协程面前没有任何优势,IO 多就协程,CPU 密集就多进程
liuminghao233
2019-12-05 22:25:53 +08:00
@qixiangyangrm
不是
什么阻塞时间长 不是你用多线程的原因
假设你有 100 万个 tcp 连接等数据 读阻塞
这里有 100 万个 socket
你难道开 100 万个线程?
有 epoll 了不会这样玩的啊
另外 io 操作你要不就用 callback
要不就牺牲点性能用协程
好处就是你的多个 io 操作的逻辑可以在同一个栈内解决
不然你就只能 callback1–>callback2->...->callbackn 一个一个回调这样
petelin
2019-12-06 00:21:45 +08:00
这个问题非常简单
xiecheng 是在线程之上的
提供的非常高级的工具

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

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

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

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

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

当年用 boost asio 里一个 coro 写过协程 场景是一个私有协议的网络编程
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
2019-12-06 16:14:27 +08:00
@ClericPy 前辈,你现在还用到的轮子有哪些,能否分享一下?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/626322

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX