asyncio.Semaphore RuntimeError: Task got Future 附加到不同的循环 [英] asyncio.Semaphore RuntimeError: Task got Future attached to a different loop
问题描述
当我在 Python 3.7 中运行此代码时:
导入异步sem = asyncio.Semaphore(2)异步定义工作():与 sem 异步:打印('工作')等待 asyncio.sleep(1)异步定义主():等待 asyncio.gather(work(), work(), work())asyncio.run(main())
它因运行时错误而失败:
$ python3 demo.py在职的在职的回溯(最近一次调用最后一次):文件demo.py",第 13 行,在 <module> 中asyncio.run(main())文件/opt/local/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/runners.py",第 43 行,运行中返回 loop.run_until_complete(main)文件/opt/local/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/base_events.py",第 584 行,在 run_until_complete 中返回 future.result()文件demo.py",第 11 行,在主目录中等待 asyncio.gather(work(), work(), work())文件demo.py",第 6 行,工作中与 sem 异步:文件/opt/local/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/locks.py",第 92 行,在 __aenter__等待 self.acquire()文件/opt/local/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/asyncio/locks.py",第 474 行,在获取中等待未来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>附加到不同的循环
这是因为 Semaphore 构造函数设置了它的 _loop
属性——在 asyncio/locks.py:
类信号量(_ContextManagerMixin):def __init__(self, value=1, *, loop=None):如果值<0:raise ValueError(信号量初始值必须> = 0")self._value = 值self._waiters = collections.deque()如果循环不是无:self._loop = 循环别的:self._loop = events.get_event_loop()
但是 asyncio.run()
在 asyncio/runners.py,它也在文档中提到:
def run(main, *, debug=False):如果 events._get_running_loop() 不是 None:引发运行时错误(无法从正在运行的事件循环中调用 asyncio.run()")如果不是 coroutines.iscoroutine(main):raise ValueError("a coroutine is expected, got {!r}".format(main))loop = events.new_event_loop()...
在 asyncio.run()
之外启动的
Semaphore
获取 asyncio 默认"循环,因此不能与使用 asyncio.run()
创建的事件循环一起使用.
解决方案
从 asyncio.run()
调用的代码中启动 Semaphore
.您必须将它们传递到正确的位置,有更多的可能性如何做到这一点,例如,您可以使用 contextvars,但我只举一个最简单的例子:
导入异步异步定义工作(sem):与 sem 异步:打印('工作')等待 asyncio.sleep(1)异步定义主():sem = asyncio.Semaphore(2)等待 asyncio.gather(工作(sem),工作(sem),工作(sem))asyncio.run(main())
asyncio.Lock
、asyncio.Event
和 asyncio.Condition
可能也存在同样的问题(和解决方案).>
When I run this code in 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())
It fails with 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
It's because Semaphore constructor sets its _loop
attribute – in asyncio/locks.py:
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()
But asyncio.run()
starts a completely new loop – in asyncio/runners.py, it's also metioned in the documentation:
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()
...
Semaphore
initiated outside of asyncio.run()
grabs the asyncio "default" loop and so cannot be used with the event loop created with asyncio.run()
.
Solution
Initiate Semaphore
from code called by asyncio.run()
. You will have to pass them to the right place, there are more possibilities how to do that, you can for example use contextvars, but I will just give the simplest example:
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())
The same issue (and solution) is probably also with asyncio.Lock
, asyncio.Event
, and asyncio.Condition
.
这篇关于asyncio.Semaphore RuntimeError: Task got Future 附加到不同的循环的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!