python3 異步錯誤 asyncio.Semaphore RuntimeError: Task got Future attached to a different loop

錯誤現象

asyncio.Semaphore RuntimeError: Task got Future attached to a different loop

asyncio.Semaphore RuntimeError:任務將Future連接到另一個循環

當我在Python 3.7中運行此代碼時:

import asyncio

sem = asyncio.Semaphore(2)

async def work():
    async with sem:
        print('working')
        await asyncio.sleep(1)

async def main():
    await asyncio.gather(work(), work(), work())

asyncio.run(main())

它因RuntimeError失敗:

$ python3 demo.py
working
working
Traceback (most recent call last):
  File "demo.py", line 13, in <module>
    asyncio.run(main())
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/runners.py", line 43, in run
    return loop.run_until_complete(main)
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/base_events.py", line 584, in run_until_complete
    return future.result()
  File "demo.py", line 11, in main
    await asyncio.gather(work(), work(), work())
  File "demo.py", line 6, in work
    async with sem:
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/locks.py", line 92, in __aenter__
    await self.acquire()
  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/locks.py", line 474, in acquire
    await fut
RuntimeError: Task <Task pending coro=<work() running at demo.py:6> cb=[gather.<locals>._done_callback() at /opt/local/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/tasks.py:664]> got Future <Future pending> attached to a different loop

錯誤原因

這是因爲Semaphore構造函數在asyncio / locks.py中設置了_loop屬性:

class Semaphore(_ContextManagerMixin):

    def __init__(self, value=1, *, loop=None):
        if value < 0:
            raise ValueError("Semaphore initial value must be >= 0")
        self._value = value
        self._waiters = collections.deque()
        if loop is not None:
            self._loop = loop
        else:
            self._loop = events.get_event_loop()

但是asyncio.run()啓動了一個全新的循環–在asyncio / runners.py中 ,它在文檔中也有提及 :

def run(main, *, debug=False):
    if events._get_running_loop() is not None:
        raise RuntimeError(
            "asyncio.run() cannot be called from a running event loop")

    if not coroutines.iscoroutine(main):
        raise ValueError("a coroutine was expected, got {!r}".format(main))

    loop = events.new_event_loop()
    ...

asyncio.run()外部啓動的Semaphore將獲取asyncio“默認”循環,因此不能與通過asyncio.run()創建的事件循環一起使用。

解決方法

asyncio.run()調用的代碼中啓動Semaphore 。 您將必須將它們傳遞到正確的位置,還有更多的方法可以做到這一點,例如,可以使用contextvars ,但是我僅給出最簡單的示例:

import asyncio

async def work(sem):
    async with sem:
        print('working')
        await asyncio.sleep(1)

async def main():
    sem = asyncio.Semaphore(2)
    await asyncio.gather(work(sem), work(sem), work(sem))

asyncio.run(main())

有多個 asyncio loop 時,在創建 asyncio.Semaphore 時需要指定使用哪個 loop,例如 asyncio.Semaphore(2, loop=xxx_loop)

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章