Asyncio How do you use run_forever?

Ada*_*kin 7 python python-asyncio

What I want to do:

  1. have an asyncio event loop that gets spun up
  2. that loop is passed to various classes in my system for scheduling coroutines on
  3. that loop is also used for handling the responses to events (ie, I have a Queue, some event handling code will place an item on that queue, and separate co-routines that await a get() on that queue to handle those values)
  4. there is a main thread which "owns" the loop and is responsible for creating the loop, and at time of system shutdown will cancel any running tasks on the loop and close & stop the loop (cleanly shutdown)

My understanding is because of #3, something needs to call run_forever() on the loop to ensure that tasks get scheduled on the loop. But if I call run_forever() then my main thread blocks, never to terminate.

What I've tried:

Spawn a thread, passing in the loop, and then call run_forever in the thread. This means though that my unit tests never finish. The gist:

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 Code Online (Sandbox Code Playgroud)

use*_*342 7

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

在不切实际的情况下,例如包含大量同步代码的程序,或者那些已经在多个线程之间进行通信的程序,创建一个专用线程并在其中运行事件循环通常是一个更好的主意。在这种情况下,您必须非常小心,不要与事件循环进行通信,而不是调用loop.call_soon_threadsafe()asyncio.run_coroutine_threadsafe()。例如,__end_background_loop必须调用 usingloop.call_soon_threadsafe(__end_background_loop)因为它与任务和事件循环交互。这适用于与事件循环的所有交互 - 例如,loop.stop()不允许从另一个线程调用,它必须拼写为loop.call_soon_threadsafe(loop.stop)。当然,从 asyncio 回调和协程调用循环函数是没问题的,因为它们将始终在运行事件循环的同一线程中运行。

  • @AdamParkin 循环可以从事件循环线程中的回调或协程中停止 - 例如,协程可以检测套接字上的“退出”命令并调用 `loop.stop()`。这就是图片中只有一个线程时的工作方式,它只运行事件循环,这是 asyncio 的一种完全有效(甚至是典型的)用法。如果你确实使用了多个线程,那么所有其他线程都必须调用 `loop.stop()` 作为 `loop.call_soon_threadsafe(loop.stop)`(注意 `loop.stop` 后缺少括号),其他线程也一样循环相关。 (2认同)