当任务添加到不同线程中的空循环时出现奇怪的行为

2024-04-23

我有一个应用程序,它将协程添加到已经运行的事件循环中。 这些协程的参数取决于 I/O,并且在我最初启动事件循环时不可用 - 使用循环运行_forever(),所以我稍后添加任务。为了演示这种现象,下面是一些示例代码:

import asyncio
from threading import Thread
from time import sleep

loop = asyncio.new_event_loop()

def foo():
    loop.run_forever()

async def bar(s):
    while True:
        await asyncio.sleep(1)
        print("s")

#loop.create_task(bar("A task created before thread created & before loop started"))
t = Thread(target=foo)
t.start()
sleep(1)
loop.create_task(bar("secondary task"))

奇怪的行为是,当调用时循环中至少有一个任务时,一切都会按预期工作循环运行_forever()。即当注释行未被注释掉时。

但是,当它被注释掉时,如上所示,不会打印任何内容,而且我似乎无法将任务添加到 event_loop。我应该避免调用永远运行()不添加单个任务?我不明白为什么这会成为一个问题。在 event_loop 运行后向其添加任务是标准做法,为什么空情况会成为问题呢?


在 event_loop 运行后向其添加任务是标准做法,为什么空情况会成为问题呢?

因为你应该添加任务从运行事件循环的线程。一般来说,不应混合使用线程和异步,除非通过为此目的设计的 API,例如loop.run_in_executor https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.AbstractEventLoop.run_in_executor.

如果您理解这一点并且仍然有充分的理由从单独的线程添加任务,请使用asyncio.run_coroutine_threadsafe https://docs.python.org/3/library/asyncio-task.html#asyncio.run_coroutine_threadsafe。改变loop.create_task(bar(...)) to:

asyncio.run_coroutine_threadsafe(bar("in loop"), loop=loop)

run_coroutine_threadsafe以线程安全的方式访问事件循环,同时也保证了事件循环wakes up注意到新任务,即使它没有任何事情可做,只是等待 IO/超时。

预先添加另一个任务似乎只起作用,因为bar恰好是一个无限协程,使得事件循环每秒都会被唤醒。一旦事件循环因任何原因唤醒,它就会执行所有可运行的任务,无论是哪个线程添加的。不过,依赖这个确实是个坏主意,因为loop.create_task不是线程安全的,因此如果它与正在运行的事件循环并行执行,可能会出现任意数量的竞争条件。

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

当任务添加到不同线程中的空循环时出现奇怪的行为 的相关文章

随机推荐