首页 文章

如何在库函数中使用asyncio事件循环

提问于
浏览
4

我正在尝试使用asyncio, users of this function should not need to know that asyncio is involved under the hood 创建一个执行某些异步操作的函数 . 我很难理解如何使用asyncio API来完成,因为大多数函数似乎在使用get_event_loop访问的某个全局循环变量下运行,并且对此调用受此循环内的全局状态的影响 .

我有四个例子,其中两个(foo1和foo3)似乎是合理的用例,但它们都表现出非常奇怪的行为:

async def bar(loop):
    # Disregard how simple this is, it's just for example
    s = await asyncio.create_subprocess_exec("ls", loop=loop)


def foo1():
    # Example1: Just use get_event_loop
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.wait_for(bar(loop), 1000))
    # On exit this is written to stderr:
    #    Exception ignored in: <bound method BaseEventLoop.__del__ of <_UnixSelectorEventLoop running=False closed=True debug=False>>
    #    Traceback (most recent call last):
    #      File "/usr/lib/python3.5/asyncio/base_events.py", line 510, in __del__
    #      File "/usr/lib/python3.5/asyncio/unix_events.py", line 65, in close
    #      File "/usr/lib/python3.5/asyncio/unix_events.py", line 146, in remove_signal_handler
    #      File "/usr/lib/python3.5/signal.py", line 47, in signal
    #    TypeError: signal handler must be signal.SIG_IGN, signal.SIG_DFL, or a callable object


def foo2():
    # Example2: Use get_event_loop and close it when done
    loop = asyncio.get_event_loop()
    loop.run_until_complete(asyncio.wait_for(bar(loop), 1000))  # RuntimeError: Event loop is closed  --- if foo2() is called twice
    loop.close()


def foo3():
    # Example3: Always use new_event_loop
    loop = asyncio.new_event_loop()
    loop.run_until_complete(asyncio.wait_for(bar(loop), 1000)) #RuntimeError: Cannot add child handler, the child watcher does not have a loop attached
    loop.close()


def foo4():
    # Example4: Same as foo3 but also set_event_loop to the newly created one
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)        # Polutes global event loop, callers of foo4 does not expect this.
    loop.run_until_complete(asyncio.wait_for(bar(loop), 1000))  # OK
    loop.close()

这些功能都不起作用,我没有看到任何其他明显的方法,asyncio应该如何使用?它似乎只是在假设应用程序的入口点是您可以创建和关闭全局循环的唯一位置的情况下使用 . 我是否必须摆弄事件循环政策?

foo3看起来像是正确的解决方案但是我得到了一个错误,即使我明确地传递了循环,因为在内部create_subprocess_exec内部,它使用当前策略来获取一个新的循环,这是一个无,这是asyncio子进程中的错误吗?

我在Ubuntu上使用Python 3.5.3 .

2 回答

  • 1

    发生错误 foo1 因为您没有关闭事件循环,请参阅issue .

    foo2 因为您无法重用已关闭的事件循环 .

    foo3 因为您没有将新的事件循环设置为全局 .

    foo4 几乎就是你想要的,你剩下要做的就是存储旧事件循环并在执行bar之后将其设置为全局:

    import asyncio
    
    
    async def bar():
        # After you set new event loop global,
        # there's no need to pass loop as param to bar or anywhere else.
        process = await asyncio.create_subprocess_exec("ls")
        await process.communicate()
    
    
    def sync_exec(coro):  # foo5
        old_loop = asyncio.get_event_loop()
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        try:
            loop.run_until_complete(coro)
        finally:
            loop.close()
            asyncio.set_event_loop(old_loop)
    
    
    sync_exec(asyncio.wait_for(bar(), 1000))
    

    One more important thing: 它's not clear why you want to hide using of asyncio behind some sync functions, but usually it'是个坏主意 . 关于一个全局事件循环的全部内容是允许用户在此单个事件循环中运行不同的并发作业 . 你试图消除这种可能性 . 我想你应该重新考虑这个决定 .

  • 0

    升级到Python 3.6,然后foo1()将工作,而无需显式关闭默认事件循环 .

    不是我希望的答案,因为我们只使用3.5 :(

相关问题