Python 的协程到底有啥用啊… - V2EX
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
nightan
V2EX    Python

Python 的协程到底有啥用啊…

  •  
  •   nightan 2020-03-23 23:11:47 +08:00 via Android 8728 次点击
    这是一个创建于 2035 天前的主题,其中的信息可能已经有所发展或是发生改变。
    看了好多文章,也自己尝试用了用协程,但是感觉对程序的运行效率并没什么太大的提升…
    我原以为我可以在通过 HTTP 取数据的这个时间内让程序去处理别的逻辑,处理完后再回来处理取回来的数据…
    可能是我太菜了…(捂脸)
    45 条回复    2020-04-13 17:13:51 +08:00
    LokiSharp
        1
    LokiSharp  
       2020-03-23 23:15:41 +08:00
    你得所有都异步非阻塞才行
    ipwx
        2
    ipwx  
       2020-03-23 23:24:17 +08:00
    ClericPy
        3
    ClericPy  
       2020-03-23 23:24:48 +08:00
    全局协程写起来有爽的地方也有不习惯的地方, 爽的基本就是全程非阻塞了, 以前多线程处理的事情全丢给 Future/Task, 整个程序基本不会因为一句代码影响其他代码(写错的情况下还是可能的... 比如错用 time.sleep). 不爽的地方就是程序不能自己识别自己要不要 await 一个结果, 害我为了兼容普通函数和协程的执行结果, 总得

    result = (await result) if isawaitable(result) else xxx

    性能的话, 不开 uvloop 感觉不出太明显差距
    mimzy
        4
    mimzy  
       2020-03-23 23:34:54 +08:00
    用同步的思维写异步程序
    减少锁、上下文切换、线程自身的开销
    mimzy
        5
    mimzy  
       2020-03-23 23:35:07 +08:00   1
    For I/O-bound workloads, there are exactly (only!) two reasons to use async-based concurrency over thread-based concurrency:

    - Asyncio offers a safer alternative to preemptive multitasking (i.e., using threads), thereby avoiding the bugs, race conditions, and other nondeterministic dangers that frequently occur in nontrivial threaded applications.

    - Asyncio offers a simple way to support many thousands of simultaneous socket connections, including being able to handle many long-lived connections for newer technologies like WebSockets, or MQTT for Internet of Things (IoT) applications.

    摘自 Using Asyncio in Python 昨天刚好看了这本书 如果感兴趣且能看到的话可以看下 还是挺不错的 http://shop.oreilly.com/product/0636920320876.do
    zhuangzhuang1988
        6
    zhuangzhuang1988  
       2020-03-24 00:16:24 +08:00
    xingheng
        7
    xingheng  
       2020-03-24 01:27:18 +08:00
    协程的意义在意执行多个没有上下文结果依赖的“不相关”的任务的时候会让这些任务并行执行,同时又不需要担心线程的状态管理,以此达到运行效率的提升。
    但是,有一种情况下协程并不会提升效率,理论上反而会降低效率(因为线程切换的代价)。

    async def run(tasks):
    all_results = []
    for task in tasks:
    result = await execute(task) # the only one await
    all_results.append(result) # append logic result of execute, not task itself

    return all_results

    不是特别好的一个例子。只有一个 await task 的话,后续操作又需要拿到结果才能继续,相当于同步的 join 卡在了 caller 所在的线程,没有异步的意义,所以会更慢。当然,基于上面的例子要优化也是非常简单的,不写了
    sylvos
        8
    sylvos  
       2020-03-24 07:15:39 +08:00 via iPhone
    @xingheng 还请写下优化的代码
    janxin
        9
    janxin  
       2020-03-24 07:54:28 +08:00
    因为没有理解并发并行的含义吧...
    YUX
        10
    YUX  
    PRO
       2020-03-24 08:20:32 +08:00 via iPhone
    https://hatboy.github.io/2019/02/16/Python 异步编程详解 /

    推荐一篇好文章
    nightan
        11
    nightan  
    OP
       2020-03-24 09:19:54 +08:00
    @xingheng 我好像就是写了个这样的东西(捂脸)
    nightan
        12
    nightan  
    OP
       2020-03-24 09:20:34 +08:00
    @YUX 大佬 404 了这个……
    nightan
        13
    nightan  
    OP
       2020-03-24 09:31:22 +08:00
    @xingheng 大佬,求优化的例子……
    jatsz
        14
    jatsz  
       2020-03-24 09:32:28 +08:00
    协程-主要还是保持状态,像你这种还是需要异步 IO,asyncio 。
    在应用上,主要还是生成器,比如你处理未知网络数据,你可以使用协程去迭代处理。
    https://www.imzjy.com/blog/2015-01-01-coroutine
    itskingname
        15
    itskingname  
       2020-03-24 09:39:35 +08:00   2
    我写了一篇文章来说明你遇到的问题。https://mp.weixin.qq.com/s/spayiLNuTnFVOWWeW-jkwQ

    注意文中为了照顾没有基础的读者,有些概念可能并不十分准确,但是表达意思。
    bnm965321
        16
    bnm965321  
       2020-03-24 10:07:16 +08:00
    当然可以在 HTTP 取数据的时间干其他的事情,就算是做 CPU bound 的事情也可以。如果你从头写一个爬虫,一次多发一些 HTTP request 就知道了。HTTP 堵塞的那些时间,CPU 是可以做很多很多事情的
    smallgoogle
        17
    smallgoogle  
       2020-03-24 10:10:51 +08:00
    正我知道协程速度快很多就对了。
    metamask
        19
    metamask  
       2020-03-24 10:27:52 +08:00
    > 这个时间内让程序去处理别的逻辑,处理完后再回来处理取回来的数据

    这个地方读起来有些怪


    之前是写了一个 chunk + requests_async 用协程处理

    你可以这么理解

    for _ in range(100):
    request


    for _ in range(100):
    request_async


    这两者会有区别。

    但如果
    你只是请求一个并直接使用,不一定有你要的效果。
    vicalloy
        20
    vicalloy  
       2020-03-24 10:28:51 +08:00
    协程是单线程的,因此你必须保证你的所有“阻塞”操作都是异步的,不然对性能没有任何提升。
    比如你用 requests 写爬虫程序。由于 requests 的操作都是阻塞的,用协程不会带来任何的性能提升。
    要想提升性能得把 requests 换成非阻塞的库,如 aiohttp 。
    YUX
        21
    YUX  
    PRO
       2020-03-24 10:35:56 +08:00
    也可以试试 multiprocessing
    nightan
        22
    nightan  
    OP
       2020-03-24 14:23:23 +08:00
    @YUX 感谢大佬的分享,前后大概看得懂……中间看的懵圈……(捂脸),我得慢慢消化消化……
    我现在写的程序中,实际上有三个函数,分别负责
    - 1. 从某个数据库读数据,存下来
    - 2. 处理这些数据,然后写到另一个库中,同时这些处理后的数据还要让第三个函数拿到
    - 3. 把处理后的数据通过 HTTP 发送出去
    也许我可以把这三个函数放到三个线程中?其中一个函数发生 I/O 操作的时候,其它函数应该会执行吧……?
    YUX
        23
    YUX  
    PRO
       2020-03-24 14:31:06 +08:00
    @nightan #22 正如前面多次提到的 要全局都非阻塞 不知道你这三个函数是否都满足要求 推荐一个 非阻塞的 orm https://github.com/tortoise/tortoise-orm
    Orenoid
        24
    Orenoid  
      2020-03-24 15:04:29 +08:00
    推荐一篇文章,https://snarky.ca/how-the-heck-does-async-await-work-in-python-3-5/
    作者从头讲解了 Python 协程的工作原理,并实现了一个简易的事件循环,有助于理解基于 Python 协程的工作原理。
    但是这个说的是原理,目前可能不太适合你看。之后有兴趣可以看下,看完可以再读下 asyncio 库的源码。
    Python 协程本质是依赖 IO 多路复用和 yield 去实现异步的,所以只有网络 IO 才有必要使用协程。
    nightan
        25
    nightan  
    OP
       2020-03-24 15:16:37 +08:00
    @YUX 发 HTTP 请求用的 requests 模块,看起来是阻塞的,刚刚我也试过,await 对于这个模块的调用不生效; 所以即使我用多线程,在处理 HTTP 请求的时候,因为是阻塞的,也一定是要等它执行完是吗?阻塞的 I/O 发生时,python 也不能切换到别的线程咯……
    crackhopper
        26
    crackhopper  
       2020-03-24 16:07:05 +08:00
    你得整体在一个异步框架里,才有意义。比如事件驱动的框架。
    metamask
        27
    metamask  
       2020-03-24 17:18:22 +08:00
    xcstream
        28
    xcstream  
       2020-03-24 17:23:07 +08:00
    无锁并发
    nightan
        29
    nightan  
    OP
       2020-03-24 19:21:53 +08:00 via Android
    @freakxx 谢谢
    hehheh
        30
    hehheh  
       2020-03-24 20:13:12 +08:00
    我以前写过一个代理 ip 池,测试 1000 个 IP 。如果是多线程大概需要 2 分钟,如果是协程大概 10 秒。
    keepeye
        31
    keepeye  
       2020-03-24 21:17:28 +08:00
    我原以为我可以在通过 HTTP 取数据的这个时间内让程序去处理别的逻辑,处理完后再回来处理取回来的数据
    ====
    你不会以为协程只能 await 吧? asyncio.ensure_feature 以及 asyncio.gather 了解过么?
    nightan
        32
    nightan  
    OP
       2020-03-24 21:43:11 +08:00
    @keepeye 看了楼上大佬分享的博客,asyncio.ensure_feature 有用到……效果明显……但是要用 requests_async,requests 本身是阻塞的似乎没办法……
    keepeye
        33
    keepeye  
       2020-03-24 21:45:39 +08:00
    @nightan aiohttp aiomysql 等等 https://github.com/aio-libs
    nightan
        34
    nightan  
    OP
       2020-03-24 23:29:01 +08:00 via Android
    @keepeye 谢谢!
    ClericPy
        35
    ClericPy  
       2020-03-25 00:00:13 +08:00
    @nightan #32 本身阻塞的就用 run_in_executor 吧, 丢一个线程让它玩儿去. 多看看 encode 和 aiolibs 里的经典库, 看文档灵光乍现, 看源码茅塞顿开
    black11black
        36
    black11black  
       2020-03-25 00:16:06 +08:00
    > 我原以为我可以在通过 HTTP 取数据的这个时间内让程序去处理别的逻辑,处理完后再回来处理取回来的数据…

    ↑ 异步确实是给你干这个的
    SmartKeyerror
        37
    SmartKeyerror  
       2020-03-25 11:12:51 +08:00
    协程的本质是保存当前函数或者例程的运行状态,并主动让出 CPU 资源,使得和当前函数处于"平级"的函数或者是例程能够得到 CPU 资源,并于原有保存点继续执行,整个过程只有少量的函数运行点恢复操作,没有重量级的线程(进程)上下文切换,所以在同样任务的情况下,协程比线程、进程拥有更高的执行效率。
    Python 的协程,对我个人而言还不如线程好用,宁愿使用 gevent patch,也不愿意使用 asyncio 。协程还是 Golang 用起来更加顺手。
    raymanr
        38
    raymanr  
       2020-03-25 11:39:11 +08:00
    我这两天也在看这个, 完全不能理解 async await 的意义...
    一般函数不也是要等他返回结果吗? 为啥要 await ...
    我不做爬虫不写服务器, 平时只和 numpy scipy pandas 打交道, 是不是异步对我其实没啥用 ?
    MinQ
        39
    MinQ  
       2020-03-25 14:52:33 +08:00
    @raymanr 这种一般都用多线程吧,把数据从原始状态转换成某种结构化形式丢去学习,我用 joblib 就比普通循环快几十条街
    lithbitren
        40
    lithbitren  
       2020-03-25 17:26:19 +08:00
    协程仅使用于类似爬虫或服务这种持续性 io 密集的程序,其他平砍算法可以解决的事情用协程就是增加心智负担。
    jwchen
        41
    jwchen  
       2020-03-25 21:00:22 +08:00
    go 的协程并发模型太漂亮了
    @SmartKeyerror
    jwchen
        42
    jwchen  
       2020-03-25 21:01:42 +08:00
    @raymanr
    一般等待返回的时候,本线程就卡住了,await 协程,会在等待运算结果的过程中自动切换运行线程中的其他协程
    nightan
        43
    nightan  
    OP
       2020-03-25 22:49:52 +08:00 via Android
    @raymanr 我这这几天看下来我能感觉到协程有用但是我现在真的是写不好…我暂时先用多线程和队列来优化代码…http 部分我保持顺序执行就好了,因为我还发现我目标的服务器是按顺序处理请求的,同时多个请求过去反而会慢…
    raymanr
        44
    raymanr  
       2020-03-26 09:17:22 +08:00
    @nightan 我暂时先搁置这个问题了, js 的 promise 我都可以理顺, async await 这种就是一直吃不懂, 始终对于异步的理解还在回调函数上面. 找不到回调函数在哪里就不知道发生了什么
    adamwong
        45
    adamwong  
       2020-04-13 17:13:51 +08:00
    @SmartKeyerror 小龙,看看我发现了什么
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2557 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 11:23 PVG 19:23 LAX 04:23 JFK 07:23
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86