Asyncio如何使用Run_Forever?

2022-03-25 00:00:00 python python-asyncio

问题描述

我想做的事:

  1. 创建一个自旋的异步事件循环
  2. 该循环被传递到我的系统中的各种类,用于在
  3. 上调度协程
  4. 该循环还用于处理对事件的响应(例如,我有一个队列,一些事件处理代码将在该队列上放置一个项,以及等待该队列上的get()来处理这些值的单独的协同例程)
  5. 有一个"拥有"循环的主线程,负责创建循环,在系统关机时将取消循环上正在运行的所有任务,并关闭&;停止循环(干净关机)

我的理解是,由于#3,需要在循环上调用run_forever()以确保任务在循环中被调度。但如果我调用run_forever(),我的主线程会阻塞,永远不会终止。

我尝试的内容:

生成一个线程,传入循环,然后在线程中调用run_forever。这意味着我的单元测试永远不会结束。要点:

def __start_background_loop(loop):
    def run_forever(loop):
        loop.run_forever()

    # because run_forever() will block the current thread, we spawn
    # a subthread to issue that call in.
    thread = Thread(target=run_forever, args=(loop,))
    thread.start()

def __end_background_loop(loop):
    for task in Task.all_tasks(loop):
        task.cancel()
    loop.stop()

解决方案

有两种可能的方法:您可以在主线程或后台线程中运行事件循环。如果在主线程中运行它,则需要将run_forever(或run_until_complete(main())或等效项)作为程序初始化的最后一步。在这种情况下,主线程将"阻塞",但这是可以的,因为它的事件循环将是活动的,并响应外部事件,从而允许程序运行。对调度协同例程和回调的事件循环的单个"阻塞"调用就是设计为运行asyncio的方式。

在这样做不切实际的情况下,例如包含大量同步代码的程序,或者已经在多个线程之间通信的程序,创建专用线程并在其中运行事件循环通常是更好的想法。在这种情况下,您必须非常小心,除了调用loop.call_soon_threadsafe()asyncio.run_coroutine_threadsafe()之外,不要与事件循环通信。例如,必须使用loop.call_soon_threadsafe(__end_background_loop)调用__end_background_loop,因为它与任务和事件循环交互。这适用于与事件循环的所有交互-例如,不允许从另一个线程调用loop.stop(),它必须拼写为loop.call_soon_threadsafe(loop.stop)。当然,从异步回调和协程调用循环函数是可以的,因为这些函数总是在运行事件循环的同一线程中运行。

相关文章