-
-
Notifications
You must be signed in to change notification settings - Fork 32.6k
Closed as not planned
Labels
pendingThe issue will be closed if no feedback is providedThe issue will be closed if no feedback is providedtopic-asynciotype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
Description
Bug report
Bug description:
In this example, loop.run_forever()
started in a thread, while new tasks are scheduled from the main thread.
I've got a piece of test code:
import asyncio
import time
from threading import Thread
def _thread(loop: asyncio.AbstractEventLoop):
print("Thread started")
try:
loop.run_forever()
except Exception as err:
print(f"Thread exception: {err}")
raise
else:
print(f"Thread is gone")
async def task(execution_time: float):
print(f"START {execution_time}")
await asyncio.sleep(execution_time)
print(f"END {execution_time}")
if __name__ == '__main__':
loop = asyncio.new_event_loop()
thread = Thread(target=_thread, args=(loop, ), name="asyncio loop thread", daemon=True)
thread.start()
loop.create_task(task(execution_time=4), name="Task 1")
time.sleep(6)
loop.create_task(task(execution_time=5), name="Task 2")
time.sleep(10)
print((asyncio.all_tasks(loop), loop.is_running()))
print((thread, thread.is_alive()))
The "Task 2" despite being successfully scheduled well after after "Task 1" completion, never gets executed:
Thread started
START 4
END 4
({<Task pending name='Task 2' coro=<task() running at /home/user/py/test/run_loop.py:16>>}, True)
(<Thread(asyncio loop thread, started daemon 135157163235008)>, True)
From the provided output it appears that the thread is running loop.run_forever()
all the time and the loop is also always running.
I'm not sure if that behavior is expected or not, because with following changes the code works as it should:
if __name__ == '__main__':
loop = asyncio.new_event_loop()
thread = Thread(target=_thread, args=(loop, ), name="asyncio loop thread", daemon=True)
thread.start()
loop.call_soon_threadsafe(lambda: loop.create_task(task(execution_time=4), name="Task 1"))
# loop.create_task(task(execution_time=4), name="Task 1")
time.sleep(6)
loop.call_soon_threadsafe(lambda: loop.create_task(task(execution_time=5), name="Task 2"))
# loop.create_task(task(execution_time=5), name="Task 2")
time.sleep(10)
print((asyncio.all_tasks(loop), loop.is_running()))
print((thread, thread.is_alive()))
Output:
Thread started
START 4
END 4
START 5
END 5
(set(), True)
(<Thread(asyncio loop thread, started daemon 130959561193152)>, True)
For me, the documentation appears to be not clear enough for this particular use case:
- https://docs.python.org/3.12/library/asyncio-eventloop.html#asyncio.loop.create_task
There is no clear indication of whether this call is thread-safe or not. A generic phrase "Almost all asyncio objects are not thread safe" from the https://docs.python.org/3.14/library/asyncio-dev.html#asyncio-multithreading doesn't quite fit there, because a task is being created\scheduled and not awaited or tackled with. - https://docs.python.org/3.12/library/asyncio-eventloop.html#asyncio.loop.run_forever
There is no clear explanation that a loop may silently stop scheduling tasks while returningTrue
fromloop.is_running()
. There is only an explanation what would happen atloop.stop()
which is not the case there.
CPython versions tested on:
3.12
Operating systems tested on:
Linux
Metadata
Metadata
Assignees
Labels
pendingThe issue will be closed if no feedback is providedThe issue will be closed if no feedback is providedtopic-asynciotype-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
Projects
Status
Done