[Python] Unexpected behavior from async/await in create_task

Discussão em 'Python' iniciado por Stack, Fevereiro 5, 2025.

  1. Stack

    Stack Membro Participativo

    I was debugging some production code in my application, I have replaced the actual code with dummy code below, but the functionlity remains same

    import asyncio

    class RequestHandler:
    def __init__(self, max_concurrent_requests):
    # Semaphore to limit the number of concurrent requests
    self.semaphore = asyncio.Semaphore(max_concurrent_requests)

    async def another_async_function(self, url):
    # This function will perform the actual async task, like a network request
    print(f"Start task for {url}")
    await asyncio.sleep(5) # Simulate network delay
    print(f"End task for {url}")
    return url

    async def fake_request(self, url):
    # Using async with the semaphore to limit concurrency
    async with self.semaphore:
    # Instead of directly using await, call and return the result of another async function
    return self.another_async_function(url)

    async def next(self, urls):
    tasks = []
    # Create and schedule tasks for each URL
    for url in urls:
    print(f"Scheduling task for {url}...")
    task = asyncio.create_task(await self.fake_request(url))
    tasks.append(task)

    # Yield the results as tasks complete
    for task in tasks:
    result = await task # Wait for task to complete and get result
    yield result # Yield result as each task completes

    async def main(self):
    urls = ["url1", "url2", "url3", "url4", "url5", "url6", "url7"]

    # Get the async generator from next() and process each result as it's yielded
    async for result in self.next(urls):
    print(f"Processed result: {result}")

    # Running the main function
    request_handler = RequestHandler(max_concurrent_requests=2) # Limit concurrency to 2
    asyncio.run(request_handler.main())


    I believe that the use of await inside create_task is wrong task = asyncio.create_task(await self.fake_request(url))

    And I was expecting to see sequential result because of this,

    But I am getting parallel execution for all the URLs, how is that possible, should not the for loop inside next method block for each URL processing, since there is an await inside create_task?

    Here is the sample result

    Scheduling task for url1...
    Scheduling task for url2...
    Scheduling task for url3...
    Scheduling task for url4...
    Scheduling task for url5...
    Scheduling task for url6...
    Scheduling task for url7...
    Start task for url1
    Start task for url2
    Start task for url3
    Start task for url4
    Start task for url5
    Start task for url6
    Start task for url7
    End task for url1
    End task for url2
    End task for url3
    End task for url4
    End task for url5
    End task for url6
    End task for url7
    Processed result: url1
    Processed result: url2
    Processed result: url3
    Processed result: url4
    Processed result: url5
    Processed result: url6
    Processed result: url7


    ...Program finished with exit code 0
    Press ENTER to exit console.


    What I am missing here? How does this code work?

    I asked ChatGpt, and that explains that use of await inside create_task is wrong, but fail to explain how this code was even able to acheive concurrent execution.

    I would also like to understand the actual wrongdoings of this code.

    The semaphore also seems to have no effect as all 7 url logs are printed in concurrent execution, while the sempahore limit is 2

    Continue reading...

Compartilhe esta Página