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
liudaqi
V2EX  ›  Python

Python 有 thread,为什么还要 asyncio 呢?

  •  
  •   liudaqi · 2017-10-08 22:21:48 +08:00 via Android · 12745 次点击
    这是一个创建于 2603 天前的主题,其中的信息可能已经有所发展或是发生改变。
    偶尔会用到 thread 做异步任务,没想到什么时候用 asyncio,这两个异步有什么差别吗,理解的不是很清楚
    35 条回复    2017-10-10 22:39:38 +08:00
    cheetah
        1
    cheetah  
       2017-10-08 22:23:20 +08:00
    是两种异步方案
    hjc4869
        2
    hjc4869  
       2017-10-08 22:31:03 +08:00
    有 urllib,为什么还要 urllib2 呢?
    PythonAnswer
        3
    PythonAnswer  
       2017-10-08 22:33:12 +08:00
    asyncio 是基于 yield 的
    thread 受限于 gil, 用处不大
    testcount
        4
    testcount  
       2017-10-08 23:06:58 +08:00 via Android
    线程和协程
    Kilerd
        5
    Kilerd  
       2017-10-08 23:21:11 +08:00
    多进程,线程,协程,异步

    这四个概念先懂了,我们再来聊聊这个问题。
    DoctorCat
        6
    DoctorCat  
       2017-10-08 23:23:58 +08:00
    asyncio: This module provides infrastructure for writing [single-threaded concurrent code using coroutines, multiplexing I/O] access over sockets and other resources, running network clients and servers, and other related primitives.

    thread: This module provides low-level primitives for working with [multiple threads (also called light-weight processes or tasks) ] — multiple threads of control sharing their global data space. For synchronization, simple locks (also called mutexes or binary semaphores) are provided.

    [] 里的含义,了解下相信你就明白了。
    Srar
        7
    Srar  
       2017-10-08 23:42:24 +08:00
    虽然没写过 Python 但是还是来回答下...是来降低上下文切换所带来的性能损失.
    aijam
        8
    aijam  
       2017-10-09 00:16:39 +08:00   ❤️ 4
    1. 虽然大家(包括楼上的 @PythonAnswer )都说 thread 受限于 GIL,但是那是对 CPU bounded tasks 而言,大多情况下 thread 还是足以应付 IO bounded tasks。
    2. 即使如此,应付 IO bounded tasks 时,thread 也存在两个问题,一是 thread overhead 开销较大,二是受到系统 max thread number 的限制。基于 coroutine 的 asyncio 有助于克服这两个问题。
    3. 然而 asyncio 复杂的 API 还是被人诟病: https://twitter.com/mitsuhiko/status/792441114561220609
    最近几年 Python3.4 以后的几个新特性都是围绕基于 coroutine 的 concurrency ( async generator in 3.6, async/await in 3.5, asyncio in 3.4 ),各个 Python 的会议也都会有关于 concurrency 话题的 talk,说明社区还在努力探索。
    NoAnyLove
        9
    NoAnyLove  
       2017-10-09 00:42:29 +08:00   ❤️ 1
    #3 楼的回答过于偏颇。并没有受限于 GIL 就让 thread 用处不大这种说法了,具体还是看应用场景,如果是计算密集的任务,为了充分利用多核,当然不会用 thread,而应该用 process。但是对于 IO 密集场景或者仅仅为了解决多路传输,那么 thread 是简单而有效的选择。而且#3 楼将两句话并发在一起,新手会以为 asyncio 不受限于 GIL 能够处理计算密集的任务。

    个人认为,两者的主要差别是可以处理并发量的能力。可以根据你需要处理的异步 IO 的数量,比如:如果有 100 个 socket,那么给每个 socket 分别创建一个 thread 来处理,现在的计算机应该都能 hold 住。但是当 socket 数量更高,并发量更大的时候,那么就应该选择使用 asyncio 了。
    keysona
        10
    keysona  
       2017-10-09 01:06:30 +08:00
    楼上两位说得很好了。

    一言不合就说 GIL 也是够了...

    &t=1141s

    也要看看使用场景好吗...
    dzmcs
        11
    dzmcs  
       2017-10-09 02:24:51 +08:00 via iPad
    @DoctorCat 方括号里说 io 复用,这个 asyncio 也是类似 epoll 可以得到网卡中断事件?如果只是在解释器内模拟,那这个东西做 io 大的并发估计也就一般吧

    thread 是否是系统线程,进入调度队列?
    scriptB0y
        12
    scriptB0y  
       2017-10-09 07:55:04 +08:00   ❤️ 1
    billion
        13
    billion  
       2017-10-09 09:15:12 +08:00   ❤️ 4
    比如你需要做三件事情,用电饭煲煮饭,用洗衣机洗衣服,用砂锅煲汤。

    如果你用 thread,那么就需要三个人,第一个人把饭放进电饭煲,然后等着它煮好。第二个人把衣服放进洗衣机,等着它洗好,第三个人把食材放进砂锅,等着汤煲好。准备工作做完以后,这三个人都会啥事不干,傻等着。

    如果你用 asyncio,那么你只需要一个人做这个事情。他先把米倒进电饭煲,打开开关开始煮饭,煮饭的过程电饭煲自己会做,不用这个人来管。所以中间的时间,他可以接下来去把衣服放进洗衣机,打开开关,洗衣机自动开始洗衣服,也不再需要他管了。于是他再去把食材放进砂锅,开始煲汤。开始煲汤以后,短时间里他还可以去看个书。如果汤漫出来了,它会听到声音,这个时候再去查看就好了。同样的,电饭煲煮饭煮好了会有提示音,洗衣机洗好了衣服也有提示音。它只需要听到提示音再去处理就好了。没有听到提示音,他就可以去做其他事情。

    综上所述,
    用 thread,做多少个事情就需要请多少个人,而且还有可能这些人会同时傻等,每一个人工资(占用的系统资源)可不便宜
    用 asyncio,只需要一个人就可以把所有事情全部做完。
    jin6220
        14
    jin6220  
       2017-10-09 09:26:47 +08:00 via iPhone
    @billion 除了傻等 工作干完就不在占资源了吧 看来没必要学 thread 啊
    fy
        15
    fy  
       2017-10-09 09:31:40 +08:00
    GIL 日常背锅
    PythonAnswer
        16
    PythonAnswer  
       2017-10-09 10:28:22 +08:00 via Android
    thread threading 就是被 gil 搞得没人用的,俺说错了?
    czheo
        17
    czheo  
       2017-10-09 10:31:13 +08:00
    @PythonAnswer 你说的没有错。
    fy
        18
    fy  
       2017-10-09 10:40:08 +08:00
    @PythonAnswer 说的没有错,但是一 thread 并不是没人用,二在这个与 asyncio 比的场景,GIL 没锅
    fy
        19
    fy  
       2017-10-09 10:40:57 +08:00
    应该说不能说全错
    PythoneerDev6
        20
    PythoneerDev6  
       2017-10-09 15:55:08 +08:00
    GIL 日常锅
    wizardforcel
        21
    wizardforcel  
       2017-10-09 21:09:46 +08:00 via Android
    @dzmcs 是系统级线程,但是只能映射到单个核。
    wizardforcel
        22
    wizardforcel  
       2017-10-09 21:12:27 +08:00 via Android
    @dzmcs 虽然都只能利用单个核,但是 asyncio 没有上下文切换,优势在这里。
    wizardforcel
        23
    wizardforcel  
       2017-10-09 21:18:09 +08:00 via Android
    另外 asyncio 也有缺陷。就是你如果用了它,所有 io api 都要用异步(支持多路复用)的版本。要不然还是卡着。这就意味着可能很多库就不能用了。
    dzmcs
        24
    dzmcs  
       2017-10-09 21:30:39 +08:00 via iPad
    @wizardforcel 看了下 thread 实现,是模拟线程,不是系统级线程,至少 2.7 的实现是模拟的
    NoAnyLove
        25
    NoAnyLove  
       2017-10-09 23:18:22 +08:00
    @PythonAnswer 不要总是甩锅给 GIL,到底是怎么得出结论没人用 threading 的啊?

    @wizardforcel 如果没有对应的 aio 库,把同步的操作丢给线程池来处理也是可以的。目前的异步文件操作( aiofile ),DNS 查询等操作,其实都是放在线程池里面的。真正能够高效处理的还是只有 socket。

    @dzmcs 不知道该说些什么。。。。。。
    DoctorCat
        26
    DoctorCat  
       2017-10-09 23:33:47 +08:00
    @dzmcs 除了 opcode 外我大 Py 从来都不自己模拟东西好伐,至少在 Linux 下都是依赖 syscall 的。所以 thread 也是 syscall,只是从 VM 层面和开发者层面封装了一些,同时也加锁保护 VM 执行栈,所以才有了 GIL。

    可以具体看一下模块源码,不是特别难懂的。
    DoctorCat
        27
    DoctorCat  
       2017-10-09 23:44:10 +08:00
    @dzmcs 回复 24 楼的观点,我不清楚你所谓的 [模拟] 指的是啥,但可以看看代码: https://github.com/python/cpython/blob/master/Modules/_threadmodule.c#L1023
    https://github.com/python/cpython/blob/731e18901484c75b60167a06a0ba0719a6d4827d/Python/thread_pthread.h#L201
    Linux 下是 pthread 实现的
    guyskk0x0
        28
    guyskk0x0  
       2017-10-09 23:51:46 +08:00 via Android
    程序通常会遇到三种瓶颈:CPU,IO,内存
    对策也有三种:多进程,多线程,异步 io/协程

    多进程能利用多核 CPU,但内存开销大。

    多线程在操作系统层面可以利用多核 CPU,但各种线程同步 /锁的问题,会导致 Python 解释器实现特别复杂,所以干脆加了个全局锁,只允许用一个核;线程在执行 io 操作阻塞时,系统会把线程挂起,把 CPU 分配给别的线程运行;内存开销比进程小。

    异步 io 相当于在 Python 中实现
    asyncio/协程相当于在 python 中实现一个内核 /调度系统,协程在进行 io 阻塞时,安排别的协程继续运行;内存开销更小。

    如果你遇到 io 瓶颈,可以用多线程和协程,协程内存开销更小,能同时跑更多任务,web 服务能同时处理更多请求。

    PS:asyncio 实现过于复杂,推荐 curio !
    guyskk0x0
        29
    guyskk0x0  
       2017-10-09 23:58:20 +08:00 via Android
    @wizardforcel 会 io 阻塞但没有异步 io 实现的库,可以用线程池封装成异步,也就是异步 io 和多线程可以一起用:P
    dzmcs
        30
    dzmcs  
       2017-10-10 01:01:35 +08:00 via iPad
    @DoctorCat
    我错了
    看了 threadmodule.c,没有 grep 到 pthread_create,看文件又这么长,以为是模拟的线程
    是系统线程,那为啥老说 python 线程性能差呢?
    PythonAnswer
        31
    PythonAnswer  
       2017-10-10 01:31:32 +08:00 via Android
    thread 模块被更高级的 threading 模块代替了。

    由于 gil 的存在,threading 并不能提高 cpu 密集运算的性能。threading 以前用于提高 io 密集型的操作速度。

    py3 新加的 asyncio 是用来做异步 io 的。

    我很久没用 threading 了,并没有不适。
    DoctorCat
        32
    DoctorCat  
       2017-10-10 11:42:27 +08:00
    @dzmcs 据说单核那个年代 Guido 在设计时偷懒了加了 GIL,没考虑过多核…
    justff
        33
    justff  
       2017-10-10 14:13:16 +08:00
    @DoctorCat 一直很费解,为什么这么多年了都不改进 cPython 的解释器
    wizardforcel
        34
    wizardforcel  
       2017-10-10 14:34:18 +08:00
    @dzmcs 因为系统级线程和处理机(核)之间还有个映射,GIL 把这些线程都限制在一个核上了。
    DoctorCat
        35
    DoctorCat  
       2017-10-10 22:39:38 +08:00
    @justff 社区有人尝试过的,都没啥成效。别指望 CPython 改了。
    其实放在 Web 开发领域也并无大碍,毕竟也胶水语言,可以放到本地线程栈去做计算密集,比如用 Cython 重写部分业务代码。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2812 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 07:25 · PVG 15:25 · LAX 23:25 · JFK 02:25
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.