如何在同步函数中调用非阻塞异步函数?

3

有没有办法在同步函数中调用异步函数而无需等待其完成?

我的目前测试:

  1. 问题: 等待test_timer_function函数完成
async def test_timer_function():
    await asyncio.sleep(10)
    return

def main():
    print("Starting timer at {}".format(datetime.now()))
    asyncio.run(test_timer_function())
    print("Ending timer at {}".format(datetime.now()))

问题:没有调用test_timer_function。
async def test_timer_function():
    await asyncio.sleep(10)
    return

def main():
    print("Starting timer at {}".format(datetime.now()))
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    asyncio.ensure_future(test_timer_function())
    print("Ending timer at {}".format(datetime.now()))

有什么建议吗?

2个回答

3

异步函数并不是在后台运行,它们总是在单个线程中运行。

这意味着当异步代码(普通的异步代码)中有并行任务时,只有在您给asyncio循环运行的机会时才会执行这些任务 - 当您的代码使用await、调用其中一个async forasync with或从作为任务运行的协程函数返回时发生这种情况。

在非异步代码中,您必须进入循环并将控制权传递给它,以便异步代码运行 - 这就是asyncio.run的作用 - 而asyncio.ensure_future则不会:此调用仅注册要在asyncio循环有时间运行的任务:但是您从未将控制权传递给异步循环而从函数返回,因此程序只是完成。

可以做的一件事是建立一个辅助线程,在其中运行asyncio代码:该线程将运行其asyncio循环,您可以通过使用全局变量和像Queues这样的正常线程数据结构与其中的任务通信。

对您的代码进行最小更改的方法如下:

import asyncio
import threading

from datetime import datetime
now = datetime.now


async def test_timer_function():
    await asyncio.sleep(2)
    print(f"ending async task at {now()}")
    return

def run_async_loop_in_thread():
    asyncio.run(test_timer_function())

def main():
    print(f"Starting timer at {now()}")
    t = threading.Thread(target=run_async_loop_in_thread)
    t.start()
    print(f"Ending timer at {now()}")
    return t

if __name__ == "__main__":
    t = main()
    t.join()
    print(f"asyncio thread exited normally at {now()}")

请在发布Python代码时包含导入行和调用函数的行,以使您的代码实际运行:这不是其他语言中可能需要的大量样板文件,并将您的片段转换为完整的、准备好运行的示例。

在控制台运行此片段时,请打印输出:

Starting timer at 2022-10-20 16:47:45.211654
Ending timer at 2022-10-20 16:47:45.212630
ending async task at 2022-10-20 16:47:47.213464
asyncio thread exited normally at 2022-10-20 16:47:47.215417

1
答案很简单:不行。它不能在单个线程中完成。
第一个问题:您的主函数是同步函数。它会停在asyncio.run(test_timer_function())这一行,直到事件循环完成其工作。
那么事件循环的唯一任务是什么呢?就是test_timer_function!该任务确实将控制权交回给事件循环,但并没有归还给调用者main!因此,如果事件循环中还有其他任务,它们将相互协作。但是只能在事件循环的任务之间进行协作,而不是在事件循环和调用者之间。
所以它将等待 10 秒钟。这里没有其他人可以利用这 10 秒钟来完成自己的工作。
第二个问题:您甚至没有运行事件循环。请查看文档中的ensure_future documentation

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接