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

异步大量请求,如何获得第一个有效的返回值?

  •  
  •   billgreen1 · 2018-07-12 13:52:34 +08:00 · 3916 次点击
    这是一个创建于 2350 天前的主题,其中的信息可能已经有所发展或是发生改变。
    情景:
    我的一个 list 中,有很多 future, 每个执行耗时不定。返回结果 None 或者 某个值。
    future1..futuren 中,第一个不为 None 的值,就是我想要的。
    如果用下面的语句
    ```python
    result = yield [future1(), future2(), ....]
    ```
    会把所有的都执行完。

    但是有可能会有这种情况:
    future1,future2 结果返回 None,future3 不为 None
    我就可以直接取 future3 的结果了。


    请教如何实现?
    16 条回复    2018-07-13 10:23:41 +08:00
    noli
        1
    noli  
       2018-07-12 14:13:32 +08:00
    这种 polling 工作肯定都是库或者框架做的,然而你又没有具体说到底用哪个异步库或者框架。
    creedowl
        2
    creedowl  
       2018-07-12 14:14:48 +08:00
    (换 js,用 promise
    billgreen1
        3
    billgreen1  
    OP
       2018-07-12 14:17:40 +08:00
    @noli 额,tornado,我是因为不想太限制
    v2xe2v
        4
    v2xe2v  
       2018-07-12 14:19:19 +08:00
    结果入队列,然后轮训?
    noli
        5
    noli  
       2018-07-12 14:24:08 +08:00
    @billgreen1
    http://www.tornadoweb.org/en/stable/queues.html

    4# @v2xe2v 思路是对的,实际上就是把结果入队列,取第一个。只不过这个队列不需要轮询这么费力而已。
    lolizeppelin
        6
    lolizeppelin  
       2018-07-12 14:31:44 +08:00 via Android
    包装一下 返回前 callback
    janxin
        7
    janxin  
       2018-07-12 15:04:33 +08:00
    你可以选择不用 yield,直接 add_done_callback
    zhuweiyou
        8
    zhuweiyou  
       2018-07-12 15:13:42 +08:00
    结果套同一个 func,判断第一个非 none 的做处理,其它的 abort
    wb14123
        9
    wb14123  
       2018-07-12 15:39:37 +08:00
    所有 future 的结果都扔到一个 BlockingQueue 里面然后从这个里面取
    bojoyzhou
        10
    bojoyzhou  
       2018-07-12 19:23:13 +08:00 via iPhone
    @zhuweiyou 感觉这个方案最便捷
    gjquoiai
        11
    gjquoiai  
       2018-07-12 19:43:12 +08:00
    我记得 pycon 2018 trio 的作者就讲了这个
    so1n
        12
    so1n  
       2018-07-12 20:21:16 +08:00
    不知道楼主的意思是不是这样的同时运行多个,然后获取返回的结果,如果是 None 忽略 不是就获取值并结束
    task_list = []
    done, pending = await asyncio.wait(task_list)
    for task in done:
    if task.result() != None:
    result = task.result()
    break
    andyholo
        13
    andyholo  
       2018-07-12 21:34:00 +08:00
    rxPY 这种需求还是用专门库吧,比自己琢磨方便多了
    billgreen1
        14
    billgreen1  
    OP
       2018-07-13 08:13:27 +08:00
    @so1n 可能是我表述不清楚,其实是这样的:如果第一个返回的是 future3 且结果不为 None,那么我还得等 future1 和 future2。1..n 是有优先级顺序的。当 future i 返回,且有结果时,可以确定的是 i+1 到 n 是可以丢弃了,但是得等 future1 到 future (i-1)的结果
    so1n
        15
    so1n  
       2018-07-13 08:39:58 +08:00 via Android
    @billgreen1 所以你获取的顺序不是按执行结束时间排序而是按原来排列好的队列?如果是这样的话,简单点直接先遍历队列 put 到 asynico.queue 里面,再依次 get 出来运行,如果运行结果非 none 就不用再 get 直接退出来
    pixcai
        16
    pixcai  
       2018-07-13 10:23:41 +08:00
    官方的 asyncio 已经提供你所需要的 API 了:
    asyncio.as_completed(fs, *, loop=None, timeout=None)
    Return an iterator whose values, when waited for, are Future instances.

    上面提到的 asyncio.wait 会等待所有的执行完,asyncio.as_completed 只要有一个执行完就立即返回,例如你要的效果:
    futures = [future1, future2, future3]
    for next_completed in asyncio.as_completed(futures):
    result = await next_completed
    if result is not None:
    break
    这样在取结果的时候,其它的 future 还在执行,应该就是你要的效果。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3550 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 04:23 · PVG 12:23 · LAX 20:23 · JFK 23:23
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.