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

2024-01-01

我有一段简单的代码让我发疯了一段时间。我已经发帖了this https://stackoverflow.com/questions/56350166/python-event-loop-does-not-work-properly-with-stdin前几天问的问题create_task不与input。现在我已经弄清楚了一些与此相关的事情。我正在一个单独的线程中运行事件循环并在其中推送任务。非常简单的代码。

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

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

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


从事件循环线程外部创建任务时,您需要使用asyncio.run_coroutine_threadsafe https://docs.python.org/3/library/asyncio-task.html#asyncio.run_coroutine_threadsafe。该函数将以线程安全的方式调度协程,并通知事件循环有新的工作要做。它还会返回一个concurrent.futures.Future https://docs.python.org/3/library/concurrent.futures.html#concurrent.futures.Future您可以使用该对象来阻止当前线程,直到结果可用。

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

Calling create_task https://docs.python.org/3/library/asyncio-task.html#asyncio.create_task是不够的,因为它不包含“唤醒”事件循环的代码。这是一个功能 - 通常不需要这种唤醒,添加它只会减慢常规单线程的使用速度。什么时候create_task从事件循环线程调用,它位于事件循环回调内部,因此事件循环在执行回调完成后重新获得控制权后可以立即检查其任务队列。但当create_task从不同的线程调用,事件循环处于睡眠状态等待 IO,所以run_coroutine_threadsafe需要将其唤醒。

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

我在文档中没有看到任何与此相关的内容。

看看并发和多线程 https://docs.python.org/3/library/asyncio-dev.html#asyncio-multithreading部分。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Python create_task 在运行事件循环中不起作用 的相关文章

随机推荐