我可能需要帮助来更好地说明这个问题。我正在通过python3.7和类(称为Worker()
)编写一个异步api接口。 Worker
有一些我想使用loop.run_in_executor()
运行的阻止方法。
我想构建一个装饰器,我可以在async
中的所有非Worker
方法之上添加,但是我一直遇到问题。
有人告诉我我需要在下面的装饰器中await
wraps()
:
def run_method_in_executor(func, *, loop=None):
async def wraps(*args):
_loop = loop if loop is not None else asyncio.get_event_loop()
return await _loop.run_in_executor(executor=None, func=func, *args)
return wraps
返回:
RuntimeWarning: coroutine 'run_method_in_executor.<locals>.wraps' was never awaited
我没有看到如何正确await
wraps()
,因为包含函数和修饰函数不是异步的。不确定这是由于对asyncio
的误解还是对装饰器的误解。
任何帮助(或帮助澄清)将不胜感激!
答案 0 :(得分:2)
Not_a_Golfer在评论中回答了我的问题。
将内部wraps()
函数从协程更改为生成器可以解决此问题:
def run_method_in_executor(func, *, loop=None):
def wraps(*args):
_loop = loop if loop is not None else asyncio.get_event_loop()
yield _loop.run_in_executor(executor=None, func=func, *args)
return wraps
编辑:
这对于IO确实非常有用,但是我还没有弄清楚如何await
产生的执行程序函数,这意味着如果我依靠修饰符,它将将创建竞争条件函数来更新其他异步函数使用的某些值。
答案 1 :(得分:1)
这是Python 3.6+的完整示例,该示例不使用3.8弃用的接口。返回loop.run_in_executor
的值可将包装的函数有效地转换为在线程中执行的 awaitable ,因此您可以await
完成它。
#!/usr/bin/env python3
import asyncio
import functools
import time
def run_in_executor(_func):
@functools.wraps(_func)
def wrapped(*args, **kwargs):
loop = asyncio.get_event_loop()
func = functools.partial(_func, *args, **kwargs)
return loop.run_in_executor(executor=None, func=func)
return wrapped
@run_in_executor
def say(text=None):
"""Block, then print."""
time.sleep(1.0)
print(f'say {text} at {time.monotonic():.3f}')
async def main():
print(f'beginning at {time.monotonic():.3f}')
await asyncio.gather(say('asdf'), say('hjkl'))
await say(text='foo')
await say(text='bar')
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
beginning at 3461039.617
say asdf at 3461040.618
say hjkl at 3461040.618
say foo at 3461041.620
say bar at 3461042.621