Python create_task 在运行事件循环时不起作用 [英] Python create_task does not work in running event loop

查看:88
本文介绍了Python create_task 在运行事件循环时不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一段简单的代码让我发疯了一段时间.几天前我发布了这个问题,问create_task 不适用于 input.现在我想出了一些与此相关的事情.我在一个单独的线程中运行事件循环并在其中推送任务.非常直接的代码.

I have a simple piece of code driving me crazy for a while. I have posted this question some days ago asking create_task is not working with input. Now I have figured out something related to this. I am running event loop in a separate thread and pushing tasks in it. Very straight forward code.

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))

除了这些日志消息外,什么都不会打印.

Nothing gets printed but these log messages.

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

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

Now if I block in event loop thread and let it run after a pause like this.

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

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

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

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

I have not seen anything regarding this in documentation. In most of the examples I have seen on internet people are creating tasks in loop from other coroutines and awaiting for them. But I think it is legal to use create tasks outside coroutine if you do not want to await for them.

推荐答案

从事件循环线程外创建任务时,需要使用 asyncio.run_coroutine_threadsafe.该函数将以线程安全的方式调度协程,并通知事件循环有新的工作要做.它还将返回一个 concurrent.futures.Future 对象,你可以用它来阻塞当前线程,直到结果可用.

When creating task from outside the event loop thread, you need to use 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 object which you can use to block the current thread until the result is available.

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

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

调用 create_task 是不够的,因为它不包含唤醒"事件循环的代码.这是一个特性 - 通常不需要这样的唤醒,添加它只会减慢常规单线程使用的速度.当从事件循环线程调用 create_task 时,它在一个事件循环回调中,因此事件循环可以在它重新获得控制权后立即检查其任务队列,当它完成执行回调时.但是当create_task从不同的线程调用时,事件循环处于休眠状态等待IO,所以需要run_coroutine_threadsafe来唤醒它.

Calling create_task is insufficient because it doesn't contain code to "wake up" the event loop. This is a feature - such wakeup is normally not needed, and adding it would just slow down the regular single-threaded use. When create_task is invoked from the event loop thread, it is inside an event loop callback, so the event loop can check its task queue as soon as it regains control, when it is done executing the callback. But when create_task is invoked from a different thread, the event loop is asleep waiting for IO, so run_coroutine_threadsafe is needed to wake it up.

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

To test this, you can create a "heartbeat" coroutine, which only contains an infinite loop that prints something and awaits asyncio.sleep(1). You'll see that the tasks created with create_task get executed along with the heartbeat, which also happens to wake up the event loop. In busy asyncio applications this effect can give the impression that create_task from another thread "works". However, this should never be relied on, as create_task fails to implement proper locking and could corrupt the event loop internals.

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

I have not seen anything regarding this in documentation.

查看并发和多线程部分.

这篇关于Python create_task 在运行事件循环时不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆