錯誤現象
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)