如何正确使用asyncio run_coroutine_threadsafe函数?

17

我正在尝试理解asyncio模块,并花费了大约一个小时的时间来使用run_coroutine_threadsafe函数,我甚至找到了一个工作示例,它按预期工作,但有一些限制。

首先,我不明白如何在主(或任何其他)线程中正确调用asyncio循环,在示例中,我使用run_until_complete调用它,并给它一个协程,使其忙于某些事情,直到另一个线程给它一个协程。我还有什么其他选择?

在实际生活中,什么情况下我必须混合使用asyncio和线程(在Python中)?由于GIL对于非IO操作,我认为asyncio应该取代Python中的线程。如果我错了,请不要生气并分享您的建议。

Python版本为3.7 / 3.8

import asyncio
import threading
import time


async def coro_func():
    return await asyncio.sleep(3, 42)


def another_thread(_loop):
    coro = coro_func()  # is local thread coroutine which we would like to run in another thread

    # _loop is a loop which was created in another thread

    future = asyncio.run_coroutine_threadsafe(coro, _loop)
    print(f"{threading.current_thread().name}: {future.result()}")
    time.sleep(15)
    print(f"{threading.current_thread().name} is Finished")


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    main_th_cor = asyncio.sleep(10)
    # main_th_cor  is used to make loop busy with something until another_thread will not send coroutine to it
    print("START MAIN")
    x = threading.Thread(target=another_thread, args=(loop, ), name="Some_Thread")
    x.start()
    time.sleep(1)
    loop.run_until_complete(main_th_cor)
    print("FINISH MAIN")

1个回答

19
首先,在主线程(或其他线程)中如何正确地调用asyncio循环我并不理解,在示例中,我使用run_until_complete来调用它,并给它一个协程来使其忙于某事,直到另一个线程给它一个协程。除此之外还有哪些选择?
这是loop.run_forever()的一个很好的用例。循环将运行并使用run_coroutine_threadsafe提交的协程提供服务。(您甚至可以从多个线程并行地提交这样的协程;您不需要实例化超过一个事件循环。)
您可以通过调用loop.call_soon_threadsafe(loop.stop)从另一个线程停止循环。
什么情况下我在现实生活中必须混合使用asyncio和线程(在Python中)?
理想情况下应该没有。但在现实世界中,它们确实会出现;例如:
当您将asyncio引入现有的大型程序中使用线程和阻塞调用,并且无法一次全部转换为asyncio时。 run_coroutine_threadsafe允许常规阻塞代码利用asyncio。
当您处理旧的“异步”API时,它们在幕后使用线程并从其他线程调用用户提供的API。有许多例子,例如Python自己的multiprocessing
当您需要调用没有async等效的阻塞函数时,例如CPU绑定函数,遗留数据库驱动程序等。这不是run_coroutine_threadsafe的用例,在这里您将使用run_in_executor,但它是混合线程和asyncio的另一个示例。

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