Fire, Forget, and Return Value in Python3.7
Fire, Forget, and Return Value in Python3.7
我有以下场景:
我有一个 python 服务器,在收到请求后,需要解析一些信息, return 尽快将结果提供给用户,然后自行清理。
我尝试使用以下逻辑来设计它:
Consumer: *==* (wait for result) *====(continue running)=====...
\ / return
Producer: *======(prase)====*=*
\
Cleanup: *==========*
我一直在尝试使用异步任务和协程来使这种情况无济于事。我尝试的所有操作都以生产者在 returning 之前等待清理完成,或者 return 终止清理而告终。
理论上我可以让消费者在向用户显示结果后调用清理,但我拒绝相信 Python 不知道如何 "fire-and-forget" 和 return.
例如,这段代码:
import asyncio
async def Slowpoke():
print("I see you shiver with antici...")
await asyncio.sleep(3)
print("...pation!")
async def main():
task = asyncio.create_task(Slowpoke())
return "Hi!"
if __name__ == "__main__":
print(asyncio.run(main()))
while True:
pass
returns:
I see you shiver with antici...
Hi!
永远不会达到 ...pation
。
我错过了什么?
asyncio.run
...
[...] creates a new event loop and closes it at the end. [...]
您的 coro,包裹在 task
中,在 main
的执行期间没有机会完成。
如果您 return Task
对象并打印它,您会看到它处于取消状态:
async def main():
task = asyncio.create_task(Slowpoke())
# return "Hi!"
return task
if __name__ == "__main__":
print(asyncio.run(main()))
# I see you shiver with antici...
# <Task cancelled coro=<Slowpoke() done, defined at [...]>>
当 main
在创建和调度任务(并打印 'Hi!' )后结束时,事件循环关闭,这会导致其中的所有 运行 任务被取消。
您需要保持事件循环 运行 直到任务完成,例如通过 await
在 main
:
async def main():
task = asyncio.create_task(Slowpoke())
await task
return task
if __name__ == "__main__":
print(asyncio.run(main()))
# I see you shiver with antici...
# ...pation!
# <Task finished coro=<Slowpoke() done, defined at [..]> result=None>
我设法使用线程而不是 asyncio 让它工作:
import threading
import time
def Slowpoke():
print("I see you shiver with antici...")
time.sleep(3)
print("...pation")
def Rocky():
t = threading.Thread(name="thread", target=Slowpoke)
t.setDaemon(True)
t.start()
time.sleep(1)
return "HI!"
if __name__ == "__main__":
print(Rocky())
while True:
time.sleep(1)
asyncio
似乎不是特别适合这个问题。您可能需要简单的线程:
这样做的原因是您的任务在父级完成时被终止。通过在那里抛出一个 daemon
线程,您的任务将继续 运行 直到它完成,或者直到程序退出。
import threading
import time
def Slowpoke():
try:
print("I see you shiver with antici...")
time.sleep(3)
print("...pation!")
except:
print("Yup")
raise Exception()
def main():
task = threading.Thread(target=Slowpoke)
task.daemon = True
task.start()
return "Hi!"
if __name__ == "__main__":
print(main())
while True:
pass
(我希望我没有正确理解你的问题。ASCII 图像和文本描述在我的脑海中并不完全对应。"Hi!"
是结果,"Antici..pation"
是清理,对吧? 我也喜欢那个音乐剧,顺便说一句)
一种可能的基于 asyncio 的解决方案是 return 尽快得到结果。 return 终止任务,这就是为什么有必要进行即发即弃清理的原因。它必须伴随着等待所有清理完成的关闭代码。
import asyncio
async def Slowpoke():
print("I see you shiver with antici...")
await asyncio.sleep(3)
print("...pation!")
async def main():
result = "Hi!"
asyncio.create_task(Slowpoke())
return result
async def start_stop():
# you can create multiple tasks to serve multiple requests
task = asyncio.create_task(main())
print(await task)
# after the last request wait for cleanups to finish
this_task = asyncio.current_task()
all_tasks = [
task for task in asyncio.all_tasks()
if task is not this_task]
await asyncio.wait(all_tasks)
if __name__ == "__main__":
asyncio.run(start_stop())
另一个解决方案是使用其他方法(不是 return)将结果传递给等待任务,这样清理就可以在解析后立即开始。 Future 被认为是低级的,但无论如何这里都是一个例子。
import asyncio
async def main(fut):
fut.set_result("Hi!")
# result delivered, continue with cleanup
print("I see you shiver with antici...")
await asyncio.sleep(3)
print("...pation!")
async def start_stop():
fut = asyncio.get_event_loop().create_future()
task = asyncio.create_task(main(fut))
print(await fut)
this_task = asyncio.current_task()
all_tasks = [
task for task in asyncio.all_tasks()
if task is not this_task]
await asyncio.wait(all_tasks)
if __name__ == "__main__":
asyncio.run(start_stop())
我有以下场景: 我有一个 python 服务器,在收到请求后,需要解析一些信息, return 尽快将结果提供给用户,然后自行清理。 我尝试使用以下逻辑来设计它:
Consumer: *==* (wait for result) *====(continue running)=====...
\ / return
Producer: *======(prase)====*=*
\
Cleanup: *==========*
我一直在尝试使用异步任务和协程来使这种情况无济于事。我尝试的所有操作都以生产者在 returning 之前等待清理完成,或者 return 终止清理而告终。 理论上我可以让消费者在向用户显示结果后调用清理,但我拒绝相信 Python 不知道如何 "fire-and-forget" 和 return.
例如,这段代码:
import asyncio
async def Slowpoke():
print("I see you shiver with antici...")
await asyncio.sleep(3)
print("...pation!")
async def main():
task = asyncio.create_task(Slowpoke())
return "Hi!"
if __name__ == "__main__":
print(asyncio.run(main()))
while True:
pass
returns:
I see you shiver with antici...
Hi!
永远不会达到 ...pation
。
我错过了什么?
asyncio.run
...
[...] creates a new event loop and closes it at the end. [...]
您的 coro,包裹在 task
中,在 main
的执行期间没有机会完成。
如果您 return Task
对象并打印它,您会看到它处于取消状态:
async def main():
task = asyncio.create_task(Slowpoke())
# return "Hi!"
return task
if __name__ == "__main__":
print(asyncio.run(main()))
# I see you shiver with antici...
# <Task cancelled coro=<Slowpoke() done, defined at [...]>>
当 main
在创建和调度任务(并打印 'Hi!' )后结束时,事件循环关闭,这会导致其中的所有 运行 任务被取消。
您需要保持事件循环 运行 直到任务完成,例如通过 await
在 main
:
async def main():
task = asyncio.create_task(Slowpoke())
await task
return task
if __name__ == "__main__":
print(asyncio.run(main()))
# I see you shiver with antici...
# ...pation!
# <Task finished coro=<Slowpoke() done, defined at [..]> result=None>
我设法使用线程而不是 asyncio 让它工作:
import threading
import time
def Slowpoke():
print("I see you shiver with antici...")
time.sleep(3)
print("...pation")
def Rocky():
t = threading.Thread(name="thread", target=Slowpoke)
t.setDaemon(True)
t.start()
time.sleep(1)
return "HI!"
if __name__ == "__main__":
print(Rocky())
while True:
time.sleep(1)
asyncio
似乎不是特别适合这个问题。您可能需要简单的线程:
这样做的原因是您的任务在父级完成时被终止。通过在那里抛出一个 daemon
线程,您的任务将继续 运行 直到它完成,或者直到程序退出。
import threading
import time
def Slowpoke():
try:
print("I see you shiver with antici...")
time.sleep(3)
print("...pation!")
except:
print("Yup")
raise Exception()
def main():
task = threading.Thread(target=Slowpoke)
task.daemon = True
task.start()
return "Hi!"
if __name__ == "__main__":
print(main())
while True:
pass
(我希望我没有正确理解你的问题。ASCII 图像和文本描述在我的脑海中并不完全对应。"Hi!"
是结果,"Antici..pation"
是清理,对吧? 我也喜欢那个音乐剧,顺便说一句)
一种可能的基于 asyncio 的解决方案是 return 尽快得到结果。 return 终止任务,这就是为什么有必要进行即发即弃清理的原因。它必须伴随着等待所有清理完成的关闭代码。
import asyncio
async def Slowpoke():
print("I see you shiver with antici...")
await asyncio.sleep(3)
print("...pation!")
async def main():
result = "Hi!"
asyncio.create_task(Slowpoke())
return result
async def start_stop():
# you can create multiple tasks to serve multiple requests
task = asyncio.create_task(main())
print(await task)
# after the last request wait for cleanups to finish
this_task = asyncio.current_task()
all_tasks = [
task for task in asyncio.all_tasks()
if task is not this_task]
await asyncio.wait(all_tasks)
if __name__ == "__main__":
asyncio.run(start_stop())
另一个解决方案是使用其他方法(不是 return)将结果传递给等待任务,这样清理就可以在解析后立即开始。 Future 被认为是低级的,但无论如何这里都是一个例子。
import asyncio
async def main(fut):
fut.set_result("Hi!")
# result delivered, continue with cleanup
print("I see you shiver with antici...")
await asyncio.sleep(3)
print("...pation!")
async def start_stop():
fut = asyncio.get_event_loop().create_future()
task = asyncio.create_task(main(fut))
print(await fut)
this_task = asyncio.current_task()
all_tasks = [
task for task in asyncio.all_tasks()
if task is not this_task]
await asyncio.wait(all_tasks)
if __name__ == "__main__":
asyncio.run(start_stop())