Мой сценарий использования требует, чтобы 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))
Поскольку executor.submit
вызывается непосредственно в функции, эта версия гарантирует, что рабочая функция запускается без ожидания запуска цикла событий.
PEP 3156 прямо заявляет, что run_in_executor
"эквивалентно wrap_future(executor.submit(callback, *args))
", обеспечивая тем самым необходимую гарантию - но 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
Этот подход привносит ненужную реализацию и сложность интерфейса, особенно в связи с тем, что необходимо использовать await
для получения future , что противоречит нормальному функционированию asyncio. run_now
включен только для полноты, и я бы не рекомендовал использовать его в производстве.