Python create_task 在 运行 事件循环中不起作用

Python create_task does not work in running event loop

我有一段简单的代码让我发疯了一段时间。我几天前发布了 this 问题,询问 create_task 不能与 input 一起工作。现在我想出了与此相关的事情。我在一个单独的线程中 运行ning 事件循环并在其中推送任务。非常直接的代码。

import asyncio
import threading


async def printer(message):
    print(f'[printer] {message}')


def loop_runner(loop):
    loop.run_forever()


if __name__ == '__main__':
    event_loop = asyncio.get_event_loop()
    t = threading.Thread(target=loop_runner, args=(event_loop,))
    t.start()

    for m in ['hello', 'world', 'foo', 'bar']:
        print(f'[loop running ?] {event_loop.is_running()}')
        event_loop.create_task(printer(m))

除这些日志消息外,没有打印任何内容。

[loop running ?] True
[loop running ?] True
[loop running ?] True
[loop running ?] True

现在,如果我在事件循环线程中阻塞并在这样的暂停后让它 运行。

def loop_runner(loop):
    time.sleep(1 / 1000)
    loop.run_forever()

一切正常,打印出来

[loop running ?] False
[loop running ?] False
[loop running ?] False
[loop running ?] False
[printer] hello
[printer] world
[printer] foo
[printer] bar

从表面上看,在 运行ning 事件循环中创建的任务似乎没有执行。但这是为什么呢?

我没有在文档中看到任何关于此的内容。在我在互联网上看到的大多数示例中,人们正在从其他协程循环创建任务并等待它们。但我认为如果你不想等待它们,在协程之外使用创建任务是合法的。

从事件循环线程外部创建任务时,您需要使用 asyncio.run_coroutine_threadsafe. That function will schedule the coroutine in a thread-safe manner and notify the event loop that there is new work to be done. It will also return a concurrent.futures.Future 对象,您可以使用该对象阻塞当前线程,直到结果可用。

From surface it looks like tasks created in running event loop do not get executed. But why is that?

调用 create_task 是不够的,因为它不包含 "wake up" 事件循环的代码。这是一个功能 - 通常不需要这样的唤醒,添加它只会减慢常规 single-threaded 使用。当从事件循环线程调用 create_task 时,它在事件循环回调中,因此事件循环可以在执行完回调后重新获得控制权后立即检查其任务队列。但是当create_task从不同的线程调用时,事件循环正在等待IO,所以需要run_coroutine_threadsafe来唤醒它。

要对此进行测试,您可以创建一个 "heartbeat" 协程,它只包含一个打印内容并等待 asyncio.sleep(1) 的无限循环。您会看到使用 create_task 创建的任务与心跳一起执行,这也恰好唤醒了事件循环。在繁忙的 asyncio 应用程序中,这种效果给人的印象是 create_task 来自另一个线程 "works"。但是,永远不要依赖这一点,因为 create_task 无法实现正确的锁定并可能破坏事件循环内部。

I have not seen anything regarding this in documentation.

看看 concurrency and multithreading 部分。

您的代码按预期运行:

It should be noted that calling this (run_forever()) causes our main thread to block indefinitely.

(https://tutorialedge.net/python/concurrency/asyncio-event-loops-tutorial/#the-run-forever-method)

另请参阅 了解解决方案(loop.call_soon_threadsafe()asyncio.run_coroutine_threadsafe())。