在 Python 3 中异步等待方法完成

Posted

技术标签:

【中文标题】在 Python 3 中异步等待方法完成【英文标题】:Asynchronically wait for a method to complete in Python 3 【发布时间】:2019-08-02 09:14:06 【问题描述】:

考虑这个简化的例子:

def get_external_thing() -> str:
    my_variable = a_blocking_operation()
    my_variable.some_other_operation()
    return my_variable

externally_gathered_thing = None
while True:
    sleep(1)
    externally_gathered_thing = get_external_thing()
    if externally_gathered_thing:
        do_something_1()
        externally_gathered_thing = None
    do_something_2()

这显然会进入循环,休眠一秒钟,然后进入get_external_thing() 并等待a_blocking_operation() 完成。只要get_external_thing() 正在运行,就不会执行任何其他操作。

如果get_external_thing() 没有完成,我想要完成的是强制我的循环继续并直接转到do_something_2()。但是,如果 get_external_thing() 已经完成并且 externally_gathered_thing 有一些价值,我希望 do_something_1() 也能被执行。

我怎样才能纯粹在 Python 中完成它?我尝试使用这个示例来学习 asyncio,但没有设法生成任何工作示例。由于项目要求,asyncio 是首选,但不是必须的。

换句话说,无论get_external_thing() 的结果如何,我都希望每秒执行一次do_something_2()(或每秒+一些小开销)。

注意:不要被while True 结构吓到,它被设计为在树莓派上连续运行:)

【问题讨论】:

get_external_thing() 完成后,您想在下一次迭代中再次重新运行它吗? 我的实际流程更复杂,但是,如果我的问题是正确的,在get_external_thing() 完成后,我希望do_something_1() 被执行,然后externally_gathered_thing 设置为无,然后@987654337 @。但同样,这只是一个演示代码。 (我通过在if 中添加externally_gathered_thing 来更新代码) 再一次,在完成之后,get_external_thing() 是否应该在下一次迭代中重新运行 是的,这将是首选流程。 TBH,在下一次迭代中不重新运行它不会有那么大的问题。 【参考方案1】:

对于此类任务,请查看concurrent.futures 模块。例如:

def get_external_thing() -> str:
    my_variable = a_blocking_operation()
    my_variable.some_other_operation()
    return my_variable

externally_gathered_thing = None
executor = concurrent.futures.ThreadPoolExecutor()
working = None

while True:
    if working is None:
        # if no work is in progress, start the external task in a bg thread
        working = executor.submit(get_external_thing)
    try:
        # wait for the external result, but no more than a second
        externally_gathered_thing = working.result(timeout=1)
        working = None
    except concurrent.futures.TimeoutError:
        # in case of timeout, proceed with our logic anyway, we'll get
        # back to waiting in the next iteration
        pass

    if externally_gathered_thing is not None:
        do_something_1()
        externally_gathered_thing = None
    do_something_2()

基于 asyncio 的解决方案是可能的,但它仍然必须在后台使用线程来等待阻塞操作(这就是 run_in_executor 的工作原理),因此它将 asyncio 的复杂性与线程的复杂性结合起来。

【讨论】:

这正是我一直在寻找的!非常感谢,Threading 似乎可以正确处理此任务。 我赞成这个答案,我会将其标记为已接受,因为在接下来的 2 小时内没有异步解决方案。 @Artur Asyncio 并非旨在处理像 a_blocking_operation() 这样的阻塞调用。您可以使用run_in_executor 将其硬塞到 asyncio 中,但它最终会在后台使用线程做同样的事情,给您带来 asyncio 和线程组合的复杂性,但没有 asyncio 的好处(取消等)。 好的,我相信了。感谢您的宝贵时间!

以上是关于在 Python 3 中异步等待方法完成的主要内容,如果未能解决你的问题,请参考以下文章

异步等待不等待完成

如何让一个方法在继续执行之前等待另一个(异步)方法完成?

如何等待异步 WCF 服务方法完成? [复制]

如何等待异步方法完成?

C#异步编程概念和使用

等待 dispatch_semaphore 以等待许多异步任务完成的正确方法