【问题标题】:Strange behaviour when task added to empty loop in different thread将任务添加到不同线程中的空循环时的奇怪行为
【发布时间】:2021-02-07 13:27:22
【问题描述】:

我有一个应用程序可以将协程添加到已经运行的事件循环中。 这些协程的参数取决于 I/O,并且在我最初使用 loop.run_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"))

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

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

【问题讨论】:

    标签: python python-asyncio


    【解决方案1】:

    在 event_loop 运行后添加任务是标准的,为什么空壳会成为问题?

    因为您应该从运行事件循环的线程中添加任务。一般来说,不应混合使用线程和异步,除非通过为此目的设计的 API,例如 loop.run_in_executor

    如果您理解这一点并且仍然有充分的理由从单独的线程添加任务,请使用asyncio.run_coroutine_threadsafe。将loop.create_task(bar(...)) 更改为:

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

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

    事先添加另一个任务似乎只是工作,因为bar 恰好是一个无限协程,它使事件循环每秒唤醒。一旦事件循环因任何原因被唤醒,它就会执行所有可运行的任务,而不管是哪个线程添加的。但是,依赖它是一个非常糟糕的主意,因为loop.create_task 不是线程安全的,所以如果它与正在运行的事件循环并行执行,可能会有任意数量的竞争条件。

    【讨论】:

      【解决方案2】:

      因为 loop.create_task 不是线程安全的,如果你设置了loop._debug = True,你应该会看到错误

      Traceback (most recent call last):
        File "test.py", line 23, in <module>
          loop.create_task(bar("secondary task"))
        File "/Users/soulomoon/.pyenv/versions/3.6.3/lib/python3.6/asyncio/base_events.py", line 284, in create_task
          task = tasks.Task(coro, loop=self)
        File "/Users/soulomoon/.pyenv/versions/3.6.3/lib/python3.6/asyncio/base_events.py", line 576, in call_soon
          self._check_thread()
        File "/Users/soulomoon/.pyenv/versions/3.6.3/lib/python3.6/asyncio/base_events.py", line 615, in _check_thread
          "Non-thread-safe operation invoked on an event loop other "
      RuntimeError: Non-thread-safe operation invoked on an event loop other than the current one
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-01-04
        • 2013-11-15
        相关资源
        最近更新 更多