1
lewis89 2019-12-05 20:35:26 +08:00 1
计算密集型使用多线程,由于 GIL 的存在 Python 只能用多进程+IPC 来同步 或者用其它的 python call Cworld 之类的线程黑魔法
IO 密集型就随意了 多线程 协程问题应该都不大,因为绝大部分时间都在 IO 那里 |
2
ClericPy 2019-12-05 20:37:36 +08:00
可协程的都协程, 不可协程的丢到 executor 里做假协程
就酱 |
3
qixiangyangrm OP |
4
PDX 2019-12-05 20:52:37 +08:00
没有场景的问题,就看你喜欢用哪个。
协程代码写异步和写同步一样,就这个区别。 |
5
vkhsyj 2019-12-05 21:05:17 +08:00
应用能不用多线程就不用多线程
|
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 好像优化了不少), 多线程已经基本可以当做一个子集来用了 |
7
conn4575 2019-12-05 21:55:20 +08:00 via Android
就 python 来说,多线程在协程面前没有任何优势,IO 多就协程,CPU 密集就多进程
|
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 一个一个回调这样 |
9
petelin 2019-12-06 00:21:45 +08:00 via iPhone
这个问题非常简单
xiecheng 是在线程之上的 提供的非常高级的工具 除非性能 功能满足不了你 否则不要用线程 go 里面一个线程你都碰不到 不照样好好的吗 |
10
wuwukai007 2019-12-06 08:11:22 +08:00 via Android
没用过协程,有个疑惑,协程如果是单线程的话,如果协成池其中一个任务挂了,是不是整个任务就挂了
|
11
lewinlan 2019-12-06 09:57:39 +08:00
去看操作系统的书,了解一下线程、调度器、systemcall 之类的东西,就能区分线程和协程了。
协程是在用户态内实现可控调度的手段,线程是在内核态上不可控的调度。 |
12
lewis89 2019-12-06 10:33:22 +08:00
@wuwukai007 #10 协程不是单线程,协程是在用户态加了一层透明调度的系统,像 Golang 这些的实现 就是将 你所有协程里面的同步调用都收集起来 遇到同步调用 全部给你收集到一个集合里面 然后用 Linux epoll 红黑树的实现,哪个协程的同步调用在 epoll 里面返回了 然后就让你这个协程到线程上面跑一跑 实际上就是在用户态 加了一层 IO 调度管理 谁调用同步 IO 了 就让谁去等着 把线程空出来 让其它没调用 IO 正等待 CPU 计算的协程跑起来 如果你是计算密集型的多线程任务 那完全没必要使用协程 计算密集型用协程几乎就是脱了裤子放屁。
协程的好处其实就是开销比线程小一点,然后对于应用来讲,由于不用使用系统调用,在语言层面的托管系统就能解决 IO 密集型的调度问题。 最后用户态的协程就有一个问题,操作系统没法对你进行内存管理,传统的多线程应用程序 内存管理实际上是托管给系统,系统采取多级缓存速度 从 cpu 的缓存 到内存缓存 到硬盘缓存 ,中间会根据调度来决定内存的操作管理。 |
13
lewis89 2019-12-06 10:34:49 +08:00
@wuwukai007 #10 这样的话 好处非常明显 就是 一个进程里面 只要有一个线程 负责跟系统进行 IO 交互,其余的线程都可以根据协程任务的需要 讲线程分配给各个协程让他们先跑起来。
|
15
lewis89 2019-12-06 10:54:55 +08:00
@altboy #14 难道不对吗? 协程目前的优势就是多了一层 IO 调度管理,在用户态将所有的 IO 调用 用一个线程就管理完了,如果是计算密集型 多个任务 使用多线程 没有毛病啊,而且内存也能由系统管理 随着任务规模增长 操作系统内存管理的优势就出来了
|
16
qixiangyangrm OP |
17
sylvos 2019-12-06 14:20:59 +08:00 via iPhone
@ClericPy 请教下大神,mysqlclient 怎么套上线程搞成异步,在 fastapi 异步函数上用,pymysql 效率不高,谢谢
|
18
zunceng 2019-12-06 15:29:09 +08:00
go 和 java 都好说语言和框架把你限制的死了
c++就比较惨 没有最佳实践 看你怎么实现 当年用 boost asio 里一个 coro 写过协程 场景是一个私有协议的网络编程 |
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 是真的好使 |
21
sylvos 2019-12-06 16:26:10 +08:00 via iPhone
@ClericPy 我现在用的 hug 做 api,切换到 fastapi,有必要吗,另外弱弱的问下 huey,redis,pandas,在异步函数里面用有问题吗
|
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 火了一把 我不是什么大神前辈, 就是个刷题找工作的可怜虫... |
24
farverfull 2019-12-06 16:59:50 +08:00 via Android
基本不用多线程,cpu 密集型用多进程,io 量大异步,量小随意。项目能用就行。
|
25
aaronhua 2019-12-07 10:10:52 +08:00
@farverfull +1 基本用多线程,量小真的随意。
|
26
reus 2019-12-07 13:52:10 +08:00
@petelin goroutine 的语义和 pthread 线程并没有区别,如果说用 goroutine 就是“不碰线程”,那用 pthread 的一样是“不碰线程”。goroutine 和 pthread 都是一个抽象层,只不过一个主流实现实现成 M:N 映射,一个主流实现成 1:1 映射。pthread 实际上也有 M:N 映射的实现。
|
27
reus 2019-12-07 13:59:36 +08:00
@lewis89 错漏百出。google go 的 goroutine 实现并不是协程,现在已经有了基于信号的抢占式调度,之前也有在函数入口处的半抢占调度,不是协作式调度,自然不是协程。调度的时机,程序员是不可控制的,在纯计算的循环里也可能调度。这种性质,不是协程具备的。
“然后对于应用来讲,由于不用使用系统调用“,大错,不存在不使用系统调用的程序。 “最后用户态的协程就有一个问题,操作系统没法对你进行内存管理,传统的多线程应用程序 内存管理实际上是托管给系统”,大错,不论什么程序,操作系统都只负责内存的映射,具体怎么用是用户程序自己决定,这个和线程没有丝毫关系。 |