The problem is that the loop in main is not waiting for the tasks to finish causing the tasks to not finish executing. Use asyncio.gather()
to launch and wait for all the coroutines to execute.
import asyncio
async def myfunc(i):
print("hello", i)
await asyncio.sleep(i)
print("world", i)
async def main():
await asyncio.gather(myfunc(2), myfunc(1))
asyncio.run(main())
The logic that you describe in the comments is more complicated and there is no function that implements it, so you have to design the logic, in this case there are 2 conditions that must happen to finish the application:
- All tasks must be launched.
- There should be no active task.
Considering this I use a Future to create a flag that indicates it, also use add_done_callback to notify me that the job has finished.
import asyncio
from collections import deque
import random
async def f(identifier, seconds):
print(f"Starting task: {identifier}, seconds: {seconds}s")
await asyncio.sleep(seconds)
print(f"Finished task: {identifier}")
return identifier
T = 3
class Manager:
def __init__(self, q):
self._q = q
self._future = asyncio.get_event_loop().create_future()
self._active_tasks = set()
self._status = False
@property
def q(self):
return self._q
def launch_task(self):
try:
identifier = self.q.pop()
except IndexError:
return False
else:
seconds = random.uniform(T - 1, T + 1)
task = asyncio.create_task(f(identifier, seconds))
self._active_tasks.add(identifier)
task.add_done_callback(self._finished_callback)
return True
async def work(self):
self._status = True
while self.launch_task():
await asyncio.sleep(T)
self._status = False
await self._future
def _finished_callback(self, c):
self._active_tasks.remove(c.result())
if not self._active_tasks and not self._status:
self._future.set_result(None)
async def main():
identifiers = deque(range(10))
manager = Manager(identifiers)
await manager.work()
if __name__ == "__main__":
asyncio.run(main())