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

关于 Python 协程的一个问题 (asyncio)

  •  1
     
  •   ltoddy ·
    ltoddy · 2018-09-24 12:29:47 +08:00 · 3794 次点击
    这是一个创建于 2263 天前的主题,其中的信息可能已经有所发展或是发生改变。
    import asyncio
    
    
    def foo(n):
        print(f'--------- foo({n}) ----------')
    
    
    async def main(loop):
        loop.call_later(0.1, foo, 1)
        loop.call_soon(foo, 2)
        loop.call_at(loop.time() + 0.2, foo, 3)
    
        await asyncio.sleep(1)
    
    
    event_loop = asyncio.get_event_loop()
    try:
        event_loop.run_until_complete(main(event_loop))
    finally:
        event_loop.close()
    

    有如上这段简单的代码.

    在 main 函数中有 await asyncio.sleep(1) ,如果不写一行, 那么 loop.call_later(0.1, foo, 1)loop.call_at(loop.time() + 0.2, foo, 3) 的结果都会看不到(sleep 的时间必须大于 delay 的时间), 这是为什么.

    我的疑问在这里, Python asyncio 的事件循环机制是什么, event_loop.run_until_complete(main(event_loop)) , 这里的 run_until_complete 指的 到底是什么 complete ?

    或者说有讲解 Python asyncio event loop 的好的资料告诉我也好.

    19 条回复    2018-09-26 00:34:22 +08:00
    lanpong
        1
    lanpong  
       2018-09-24 13:30:30 +08:00 via iPhone   ❤️ 2
    http://lotabout.me/2017/understand-python-asyncio/
    早几个月看到的,觉得不错
    sujin190
        2
    sujin190  
       2018-09-24 13:44:34 +08:00 via Android
    main 函数返回,不是所有事件完成,没有 sleep,main 函数返回 ioloop 就结束了啊,call_later 自然不会执行
    guyskk0x0
        3
    guyskk0x0  
       2018-09-24 13:47:40 +08:00 via Android
    显然 run_until_complete 指的是你传入的 main()结束
    so1n
        4
    so1n  
       2018-09-24 13:53:33 +08:00   ❤️ 1
    可以看下这个: https://docs.python.org/3/library/asyncio-eventloop.html
    run_until_complete 是指一个 future 运行为一个周期,也就是你传入的 main(event_loop),当没加入 await asynico.sleep()时,run_until_complete 会直接运行完 main(event_loop)然后就执行到你的 event_loop.close()直接关闭事件循环。而你代码中的 loop.call_*是把任务转交给 event_loop 运行,此时的 event_loop 已经关闭,就无法运行了。你可以使用 run_forever()保持事件循环不关闭
    ltoddy
        5
    ltoddy  
    OP
       2018-09-24 14:16:15 +08:00
    @so1n 也就是说 我另外的 两个 `loop.call_*` 是另外的两个 future ,对吧.
    zhzer
        6
    zhzer  
       2018-09-24 15:47:36 +08:00
    其实道理不难,就是绕人,我就不献丑了
    推荐《 fluent python 》虽然只用了一章讲协程,不过够了
    ltoddy
        7
    ltoddy  
    OP
       2018-09-24 16:13:19 +08:00
    @zhzer 流畅的 py 我已经读了两遍了.
    so1n
        8
    so1n  
       2018-09-24 16:23:10 +08:00   ❤️ 1
    @ltoddy 恩恩,差不多可以这样理解,不过 await asyncio.wait(tasks)就不一样了还是属于 main 的 future。
    ltoddy
        9
    ltoddy  
    OP
       2018-09-24 17:51:14 +08:00
    @so1n 谢谢了.
    neoblackcap
        10
    neoblackcap  
       2018-09-24 18:00:16 +08:00   ❤️ 1
    为什么难理解,其实是大家看书查资料的方向错了。这里虽然是协程,但更多的是事件驱动编程,除了用上了 await 来解决以前的装饰器,还有一些默认的封装。但本质还是事件驱动编程。具体可以看 linux 网络编程,跟 epoll 相关的内容。现在大多数事件循环在 Linux 下都是封装 epoll,将对应的套接字以及回调函数注册到 epoll 实例上。抓住本质自然就会了解。其实很多现在这些框架的作者是默认你了解这部分内容的。
    PythonAnswer
        11
    PythonAnswer  
       2018-09-25 07:49:20 +08:00 via iPhone
    aio 建议从 3.4 走一遍。那时还没有 await。这玩意其实就是 yield 和 yield from。搞懂了就 ok 了。

    不过用了一遍之后,可能还是大坑。

    为啥不用 gevent 呢?
    ltoddy
        12
    ltoddy  
    OP
       2018-09-25 10:36:21 +08:00
    @neoblackcap 我觉得你说的很虚, linux 的 epoll 我知道,当初在看 node 以及 libuv 的时候学了.

    我想反问,如果你会汇编,难道所有语言对你都是小 case 吗. 所以啊,别说的这么轻浮.
    ltoddy
        13
    ltoddy  
    OP
       2018-09-25 10:37:20 +08:00
    @PythonAnswer 你说的这个我在读流畅的 py 的时候就已经很熟悉了. 为什么不用 gevent, 很简单,我现在还是学生.
    PythonAnswer
        14
    PythonAnswer  
       2018-09-25 12:19:36 +08:00 via iPhone
    是学生就好好学吧。加油
    zhzer
        15
    zhzer  
       2018-09-25 13:59:06 +08:00 via Android
    @ltoddy 额,那不如看看 asyncio 源码吧,我觉得应该是没有预激的原因
    说不定是个 bug,研究清楚可以提个 issue

    或者先用最新的再试试,3.7 改了很多接口,兴许解决了这些逻辑问题
    ltoddy
        16
    ltoddy  
    OP
       2018-09-25 14:39:56 +08:00
    @zhzer 额,我就是用的 Python3.7, 准备这两天看一下资料,然后总结一下.
    cosven
        17
    cosven  
       2018-09-25 14:55:15 +08:00   ❤️ 1
    认同 so1n 的说法

    之前写过一个非常简单(残疾)地 gevent demo,一百行左右,感觉可以帮助楼主理解 asyncio/gevent 等
    https://gist.github.com/cosven/a251ca10c6c0c57c8b5dbd92fe131c2f

    在 LZ 的例子中:main 是个协程,另外 call_later/call_soon/call_at 也会创建协程。后来,run_until_complete 只等待 main 结束,就关闭了 event_loop,当 event_loop 关闭了,其它协程自然就不会执行了。

    如果 LZ 想让这几个协程都能执行完,可以用 loop.run_forever() 或者一些 asyncio.wait 等其它方法。
    ltoddy
        18
    ltoddy  
    OP
       2018-09-25 15:32:51 +08:00
    @cosven 恩,没错呢.
    lolizeppelin
        19
    lolizeppelin  
       2018-09-26 00:34:22 +08:00 via Android
    这玩意就是模仿现在 js dart 之类的 异步对象

    python 以前也有个别扭的 future 库

    他们要实现的功能都差不多 js dart 是原声实现 。
    Python 靠 yied 实现

    但是万变不离其宗 搞清楚目的就很好理解了

    Python3 我没用过 瞎说的 2333
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5870 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 32ms · UTC 02:36 · PVG 10:36 · LAX 18:36 · JFK 21:36
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.