在Python3.7中触发,遗忘和返回值

时间:2019-08-08 07:15:14

标签: python python-asyncio python-3.7 producer-consumer fire-and-forget

我有以下情况: 我有一个python服务器,收到请求后,需要解析一些信息,将结果尽快返回给用户,然后自行清理。 我尝试使用以下逻辑进行设计:

Consumer: *==*   (wait for result)   *====(continue running)=====...
              \                     / return
Producer:      *======(prase)====*=*
                                  \
Cleanup:                           *==========*

我一直在尝试使用异步任务和协程使这种情况下无济于事。我尝试过的所有结果最终都是生产者在返回之前等待清理完成,或者是返回会终止清理。 从理论上讲,我可以让使用者在将结果显示给用户后调用清除操作,但是我拒绝相信Python不知道如何“抛弃并返回”。

例如,此代码:

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

返回:

I see you shiver with antici...
Hi!

并且永远不会到达...pation

我想念什么?

4 个答案:

答案 0 :(得分:2)

我设法使用线程而不是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)

答案 1 :(得分:2)

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

答案 2 :(得分:0)

asyncio.run ...

  

[...]创建一个新的事件循环,并在最后将其关闭。 [...]

task包裹的Coro在执行main时没有机会完成。
如果返回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!”)后结束时,事件循环关闭,这会导致其中所有正在运行的任务被取消。

您需要保持事件循环运行,直到任务完成为止,例如通过awaitmain中添加它:

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>

答案 3 :(得分:0)

(我希望我正确理解了您的问题。ASCII图像和文本描述在我脑海中并不完全对应。"Hi!"是结果,"Antici..pation"是清理,对吗?就像音乐剧一样,顺便说一句)

可能的基于异步的解决方案之一是尽快返回结果。返回将终止任务,这就是为什么有必要触发并忘记清除的原因。它必须伴有关闭代码,以等待所有清理结束。

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())

另一种解决方案是使用其他方法(不返回)将结果传递给等待的任务,因此清除可以在解析后立即开始。期货被认为是低级的,但是无论如何这是一个例子。

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())