“即发即弃”python async/await
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/37278647/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
"Fire and forget" python async/await
提问by Mike N
Sometimes there is some non-critical asynchronous operation that needs to happen but I don't want to wait for it to complete. In Tornado's coroutine implementation you can "fire & forget" an asynchronous function by simply ommitting the yield
key-word.
有时需要进行一些非关键的异步操作,但我不想等待它完成。在 Tornado 的协程实现中,您可以通过简单地省略yield
关键字来“触发并忘记”异步函数。
I've been trying to figure out how to "fire & forget" with the new async
/await
syntax released in Python 3.5. E.g., a simplified code snippet:
我一直在试图弄清楚如何使用Python 3.5 中发布的新async
/await
语法“触发并忘记” 。例如,一个简化的代码片段:
async def async_foo():
print("Do some stuff asynchronously here...")
def bar():
async_foo() # fire and forget "async_foo()"
bar()
What happens though is that bar()
never executes and instead we get a runtime warning:
但是发生的事情是bar()
永远不会执行,而是我们收到运行时警告:
RuntimeWarning: coroutine 'async_foo' was never awaited
async_foo() # fire and forget "async_foo()"
回答by Mikhail Gerasimov
Upd:
更新:
Replace asyncio.ensure_future
with asyncio.create_task
everywhere if you're using Python >= 3.7 It's newer, nicer way to spawn task.
如果您使用 Python >= 3.7,请替换asyncio.ensure_future
为asyncio.create_task
任何地方 这是生成 task的更新、更好的方法。
asyncio.Task to “fire and forget”
asyncio.Task 以“即发即忘”
According to python docs for asyncio.Task
it is possible to start some coroutine to execute "in background". The task created by asyncio.ensure_future
functionwon't block the execution (therefore the function will return immediately!). This looks like a way to “fire and forget” as you requested.
根据 python 文档,asyncio.Task
可以启动一些协程以“在后台”执行。asyncio.ensure_future
函数创建的任务不会阻塞执行(因此函数会立即返回!)。这看起来像是一种按照您的要求“解雇并忘记”的方法。
import asyncio
async def async_foo():
print("async_foo started")
await asyncio.sleep(1)
print("async_foo done")
async def main():
asyncio.ensure_future(async_foo()) # fire and forget async_foo()
# btw, you can also create tasks inside non-async funcs
print('Do some actions 1')
await asyncio.sleep(1)
print('Do some actions 2')
await asyncio.sleep(1)
print('Do some actions 3')
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Output:
输出:
Do some actions 1
async_foo started
Do some actions 2
async_foo done
Do some actions 3
What if tasks are executing after event loop complete?
如果事件循环完成后任务正在执行怎么办?
Note that asyncio expects task would be completed at the moment event loop completed. So if you'll change main()
to:
请注意, asyncio 期望任务将在事件循环完成时完成。因此,如果您将更main()
改为:
async def main():
asyncio.ensure_future(async_foo()) # fire and forget
print('Do some actions 1')
await asyncio.sleep(0.1)
print('Do some actions 2')
You'll get this warning after the program finished:
程序完成后,您将收到此警告:
Task was destroyed but it is pending!
task: <Task pending coro=<async_foo() running at [...]
To prevent that you can just await all pending tasksafter event loop completed:
为了防止这种情况,您可以在事件循环完成后等待所有挂起的任务:
async def main():
asyncio.ensure_future(async_foo()) # fire and forget
print('Do some actions 1')
await asyncio.sleep(0.1)
print('Do some actions 2')
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
# Let's also finish all running tasks:
pending = asyncio.Task.all_tasks()
loop.run_until_complete(asyncio.gather(*pending))
Kill tasks instead of awaiting them
杀死任务而不是等待它们
Sometimes you don't want to await tasks to be done (for example, some tasks may be created to run forever). In that case, you can just cancel() them instead of awaiting them:
有时您不想等待任务完成(例如,某些任务可能被创建为永远运行)。在这种情况下,您可以直接取消()它们而不是等待它们:
import asyncio
from contextlib import suppress
async def echo_forever():
while True:
print("echo")
await asyncio.sleep(1)
async def main():
asyncio.ensure_future(echo_forever()) # fire and forget
print('Do some actions 1')
await asyncio.sleep(1)
print('Do some actions 2')
await asyncio.sleep(1)
print('Do some actions 3')
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
# Let's also cancel all running tasks:
pending = asyncio.Task.all_tasks()
for task in pending:
task.cancel()
# Now we should await task to execute it's cancellation.
# Cancelled task raises asyncio.CancelledError that we can suppress:
with suppress(asyncio.CancelledError):
loop.run_until_complete(task)
Output:
输出:
Do some actions 1
echo
Do some actions 2
echo
Do some actions 3
echo
回答by nehem
Thank you Sergey for the succint answer. Here is the decorated version of the same.
谢谢谢尔盖简洁的回答。这是相同的装饰版本。
import asyncio
import time
def fire_and_forget(f):
def wrapped(*args, **kwargs):
return asyncio.get_event_loop().run_in_executor(None, f, *args, *kwargs)
return wrapped
@fire_and_forget
def foo():
time.sleep(1)
print("foo() completed")
print("Hello")
foo()
print("I didn't wait for foo()")
Produces
生产
>>> Hello
>>> foo() started
>>> I didn't wait for foo()
>>> foo() completed
回答by Sergey Gornostaev
This is not entirely asynchronous execution, but maybe run_in_executor()is suitable for you.
这并不完全是异步执行,但也许run_in_executor()适合您。
def fire_and_forget(task, *args, **kwargs):
loop = asyncio.get_event_loop()
if callable(task):
return loop.run_in_executor(None, task, *args, **kwargs)
else:
raise TypeError('Task must be a callable')
def foo():
#asynchronous stuff here
fire_and_forget(foo)
回答by nehem
For some reason if you are unable to use asyncio
then here is the implementation using plain threads
出于某种原因,如果您无法使用,asyncio
那么这里是使用普通线程的实现
import threading
def fire_and_forget(f):
def wrapped():
threading.Thread(target=f).start()
return wrapped
@fire_and_forget
def foo():
time.sleep(1)
print("foo() completed")
print("Hello")
foo()
print("I didn't wait for foo()")