Search code examples
pythonlistiterationpython-asyncio

The problem is related to the async module


my code:

import asyncio


async def count(counter):
    print(f"number of entries in the list {len(counter)}")

    while True:
        await asyncio.sleep(1 / 1000)
        counter.append(1)


async def print_every_sec(counter):
    while True:
        await asyncio.sleep(1)
        print(f"- 1 secund later. " f"number of entries in the list: {len(counter)}")


async def print_every_5_sec():
    while True:
        await asyncio.sleep(5)
        print(f"---- 5 secund later")


async def print_every_10_sec():
    while True:
        await asyncio.sleep(10)
        print(f"---------- 10 secund later")


async def main():
    counter = list()

    tasks = [
        count(counter),
        print_every_sec(counter),
        print_every_5_sec(),
        print_every_10_sec(),
    ]
    await asyncio.gather(*tasks)

asyncio.run(main())

This is my conclusion but is not correct. Correct conclusion around 1000 for each iteration. I don't now what is it. This code works fine in online interpretations.


Solution

  • The assumption that asyncio.sleep(1 / 1000) (and to return control to other async routines) takes exactly one millisecond is not true.

    Here's a more interesting example that records how long the sleep (and the time.perf_counter_ns() invocation) actually took:

    import asyncio
    import statistics
    import time
    
    max_count = 2500
    
    
    async def count(counter):
        while len(counter) < max_count:
            t0 = time.perf_counter_ns()
            await asyncio.sleep(1 / 1000)
            t1 = time.perf_counter_ns()
            counter.append((t1 - t0) / 1_000_000)
    
    
    async def print_every_sec(counter):
        while len(counter) < max_count:
            await asyncio.sleep(1)
            print(f'count: {len(counter)}; average: {statistics.mean(counter)} ms')
    
    
    async def main():
        counter = list()
    
        tasks = [
            count(counter),
            print_every_sec(counter),
        ]
        await asyncio.gather(*tasks)
    
    
    asyncio.run(main())
    

    On my Macbook, Python 3.9, etc, etc., the result is

    count: 744; average: 1.341670
    count: 1494; average: 1.33668
    count: 2248; average: 1.33304
    count: 2500; average: 1.325463428
    

    so it takes 30% more than we expected to.

    For sleeps of 10ms, the average is 11.84 ms. For sleeps of 100ms, the average is 102.9 ms.