Chr*_*unt 7 python python-asyncio
我有一个服务器应用程序,当客户要求我安排一些工作,如
def work():
time.sleep(5)
fut = asyncio.get_event_loop().run_in_executor(None, work)
Run Code Online (Sandbox Code Playgroud)
我await fut后来明确要求它.我的用例要求立即run_in_executor提交work函数,并且在我的环境中表现如预期(Ubuntu 16.04,Python 3.7.1).
由于我的应用程序依赖于这种行为,我想验证它不是可能改变的东西,所以我检查了几个资源:
asyncio.Future明确的.run_in_executor是一个协程.run_in_executor返回等待或协程本身的函数似乎被视为实现细节.见25675和32327.AbstractEventLoop.run_in_executor被指定为协程,但是实现BaseEventLoop.run_in_executor是一个普通的函数.1和2似乎主要表明当前的行为是正确的,但3和4是有关的.这似乎是界面的一个非常重要的部分,因为如果函数本身是一个协程,那么它将不会开始执行(因此不会安排工作),直到等待它为止.
依赖当前的行为是否安全?如果是这样,将接口更改AbstractEventLoop.run_in_executor为普通函数而不是协程是否合理?
我的用例要求
run_in_executor立即提交工作函数,并且其行为在我的环境中符合预期
文档不保证当前的行为,文档仅指定该函数安排被调用,并且它返回一个可等待的。如果它是用协程实现的,则只有在事件循环运行时才会提交。func
然而,这种行为从一开始就存在,并且在未来极不可能改变。尽管文档在技术上允许延迟提交,但会破坏许多现实世界的异步应用程序并构成严重的向后不兼容的更改。
如果您想确保任务启动时不依赖于未记录的行为,您可以创建自己的与run_in_executor. 它实际上归结为结合executor.submit和asyncio.wrap_future。没有多余的装饰,它可以很简单:
def my_run_in_executor(executor, f, *args):
return asyncio.wrap_future(executor.submit(f, *args))
Run Code Online (Sandbox Code Playgroud)
因为executor.submit是直接在函数中调用,所以这个版本保证了worker函数在不等待事件循环运行的情况下就启动了。
PEP 3156明确指出run_in_executor“相当于wrap_future(executor.submit(callback, *args))”,从而提供了所需的保证 - 但 PEP 不是官方文档,最终的实现和规范通常与最初的 PEP 有所不同。
如果坚持遵循 的记录接口run_in_executor,也可以使用显式同步来强制协程等待工作线程启动:
async def run_now(f, *args):
loop = asyncio.get_event_loop()
started = asyncio.Event()
def wrapped_f():
loop.call_soon_threadsafe(started.set)
return f(*args)
fut = loop.run_in_executor(None, wrapped_f)
await started.wait()
return fut
fut = await run_now(work)
# here the worker has started, but not (necessarily) finished
result = await fut
# here the worker has finished and we have its return value
Run Code Online (Sandbox Code Playgroud)
这种方法引入了不必要的实现和接口复杂性,特别是需要使用await来获取future,这与 asyncio 的正常工作方式背道而驰。run_now包含它只是为了完整性,我不建议在生产中使用它。