Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

python asyncio fails to concurrently run two infinite functions

I am been trying to run two functions simultaneously but one never seems to work unless I stop the other. The first function sends an email every 30 seconds while the second prints a simple statement every 5 seconds. In combination, per every 6 "Hello Worlds" outputs one email should be sent.

However, I never get an email unless the printing is changed to stop early, such as ending after 10 seconds. What can I do to have both running concurrently without stopping?

async def timer():
    end = time.time() + 30
    while True:
        if time.time() >= end:
            sendmail(name, filepath + "\\" + name, receiver)
            end = time.time() + 30

async def runs():
    while True:
        print("Hello World")
        time.sleep(5)


loop = asyncio.get_event_loop()
loop.create_task(runs())
loop.create_task(timer())
loop.run_forever()
like image 827
Volapiik Vyrient Avatar asked Oct 20 '25 10:10

Volapiik Vyrient


1 Answers

Python's async coroutines are meant for cooperative concurrency. That means that coroutines must actively allow others to run. For simple cases, use await asyncio.sleep to pause the current coroutine and run others.

async def timer():
    while True:
        await asyncio.sleep(30)  # instead of `if time.time() >= end:…`
        sendmail(name, filepath + "\\" + name, receiver)

async def runs():
    while True:
        print("Hello World")
        await asyncio.sleep(5)  # instead of `time.sleep(5)`

async def main():
    await asyncio.gather(timer(), runs())

asyncio.run(main())

Notably, do not use time.sleep – this blocks the entire thread, meaning the current coroutine as well as the event loop and all other coroutines, until the sleep is over.
Similarly, avoid any synchronous code with significant runtime – asyncio cannot switch to other coroutines while synchronous code runs. If needed, use an asyncio helper to run synchronous code in a thread, e.g. asyncio.to_thread or loop.run_in_executor.

async def timer():
    next_run = time.time()
    while True:
        # run blocking function in thread to keep event-loop free
        await asyncio.to_thread(
            sendmail, name, filepath + "\\" + name, receiver
        )
        # pause to run approx every 30 seconds
        await asyncio.sleep(next_run - time.time())
        next_run += 30
like image 63
MisterMiyagi Avatar answered Oct 22 '25 03:10

MisterMiyagi



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!