Home > database >  How to run multiple async functions alternately without sleeping in Python?
How to run multiple async functions alternately without sleeping in Python?

Time:11-08

When running the code with await asyncio.sleep(1) below:

import asyncio

async def test1():
    for _ in range(0, 3):
        print("Test1")
        await asyncio.sleep(1) # Here
        
async def test2():
    for _ in range(0, 3):
        print("Test2")
        await asyncio.sleep(1) # Here
        
async def test3():
    for _ in range(0, 3):
        print("Test3")
        await asyncio.sleep(1) # Here

async def call_tests():
    await asyncio.gather(test1(), test2(), test3())

asyncio.run(call_tests())

test1(), test2() and test3() are run alternately sleeping 1 second each time as shown below:

Test1
Test2
Test3
Test1
Test2
Test3
Test1
Test2
Test3

Now, I want to run them alternately without sleeping but if I remove await asyncio.sleep(1) from them:

# ...

async def test1():
    for _ in range(0, 3):
        print("Test1")
        # await asyncio.sleep(1)
        
async def test2():
    for _ in range(0, 3):
        print("Test2")
        # await asyncio.sleep(1)
        
async def test3():
    for _ in range(0, 3):
        print("Test3")
        # await asyncio.sleep(1)

# ...

They are run serially as shown below:

Test1
Test1
Test1
Test2
Test2
Test2
Test3
Test3
Test3

So, how can I run them alternately without sleeping?

CodePudding user response:

await asyncio.sleep(0) which sleeps 0 second can run them alternately without sleeping as shown below:

import asyncio

async def test1():
    for _ in range(0, 3):
        print("Test1")
        await asyncio.sleep(0) # Here
        
async def test2():
    for _ in range(0, 3):
        print("Test2")
        await asyncio.sleep(0) # Here
        
async def test3():
    for _ in range(0, 3):
        print("Test3")
        await asyncio.sleep(0) # Here

async def call_tests():
    await asyncio.gather(test1(), test2(), test3())

asyncio.run(call_tests())

This is the result:

Test1
Test2
Test3
Test1
Test2
Test3
Test1
Test2
Test3

CodePudding user response:

The way to do this, as put by different people in the comments, is to call await asyncio.sleep(0). What maters is not the sleep call: with 0, it will indeed not pause, and resume the code immediately if there are no pending tasks in the event loop.

What really matters is that one have to explicitly return control to the event loop temporarily, by means of awaiting a co-routine or other awaitable object. Without an await <xxx> statement, the code execution is never transferred to the event loop, and there is no way for the asyncio execution model to run any code in other task: the model is single threaded, what it does is having the asyncio loop alternate across tasks whenever it is executing. And awaiting something is the way to pass control to the event loop. Now, it takes that if you are not really waiting for some async function to complete, and just want to pass control to the event loop to give other tasks a chance to run, there is no specific call for that - so asyncio.sleep(0) is used. It is used even in the official asyncio code inside Python's standard library. ex.: https://github.com/python/cpython/blob/main/Lib/asyncio/streams.py#L376

While one is at it, it is equally simple to check for the actual asyncio.sleep implementation, and see that values of 0 and negative do not cause any sleep: just cause the control to flow to the asyncio.loop: https://github.com/python/cpython/blob/1438b779971605e516bd0a4051a704d6ffbbd58d/Lib/asyncio/tasks.py#L630

  • Related