簡體   English   中英

如何在庫函數中使用asyncio事件循環

[英]How to use asyncio event loop in library function

我正在嘗試創建一個使用asyncio執行一些異步操作的函數,該函數的用戶不需要知道asyncio是在后台進行的 我很難理解如何使用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的內部,它正在使用當前策略來獲取一個新的循環,即None,這是asyncio子進程中的錯誤嗎?

我在Ubuntu上使用Python 3.5.3。

由於未關閉事件循環而發生foo1錯誤,請參見此問題

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))

還有一件事重要:目前尚不清楚為什么要在某些同步功能后面隱藏asyncio的用法,但這通常是個壞主意。 一個全局事件循環的全部目的是允許用戶在此單個事件循環中運行不同的並發作業。 您正在嘗試消除這種可能性。 我認為您應該重新考慮這個決定。

升級到Python 3.6,然后foo1()即可工作,而無需顯式關閉默認事件循環。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM