>백엔드 개발 >파이썬 튜토리얼 >Asyncio를 사용하여 작업 생성 및 관리

Asyncio를 사용하여 작업 생성 및 관리

王林
王林원래의
2024-08-15 06:37:08584검색

Creating and Managing Tasks with Asyncio

Asyncio를 사용하면 개발자가 번거로움 없이 Python으로 비동기 프로그램을 작성할 수 있습니다. 또한 이 모듈은 다양한 비동기 작업 방법을 제공하며 이를 수행하는 방법이 다양하므로 어떤 방법을 사용해야 할지 혼란스러울 수 있습니다.

이 글에서는 asyncio로 작업을 생성하고 관리할 수 있는 다양한 방법에 대해 논의하겠습니다.

비동기 작업이란 무엇입니까?

asyncio에서 태스크는 코루틴을 래핑하고 이벤트 루프 내에서 실행되도록 예약하는 객체입니다. 간단히 말해서 작업은 다른 작업과 동시에 코루틴을 실행하는 방법입니다. 작업이 생성되면 이벤트 루프가 해당 작업을 실행하고 필요에 따라 작업을 일시 중지하고 재개하여 다른 작업이 실행될 수 있도록 합니다.

Asyncio 작업을 생성하고 관리하는 방법

이제 작업 생성 및 관리 방법에 대해 논의해 보겠습니다. 먼저, asyncio를 사용하여 Python에서 작업을 생성하려면 다음 인수를 사용하는 asyncio.create_task 메서드를 사용합니다.

  • coro(필수): 예약할 코루틴 개체입니다. 비동기적으로 실행하고 싶은 기능입니다.

  • 이름(선택 사항): 디버깅 또는 로깅 목적에 유용할 수 있는 작업의 이름입니다. 이 매개변수에 문자열을 할당할 수 있습니다.

    • Task.set_name(name) 및 Task.get_name()을 사용하여 나중에 이름을 설정하거나 가져올 수도 있습니다.
  • 컨텍스트(선택 사항): Python 3.11에 도입되었으며 작업에 대한 컨텍스트 변수를 설정하여 작업 로컬 저장소를 활성화하는 데 사용됩니다. 스레드 로컬 저장소와 유사하지만 asyncio 작업에 사용됩니다.

    • 이 인수는 컨텍스트 관리가 필요한 고급 시나리오를 처리하지 않는 한 일반적으로 사용되지 않습니다.

다음은 asyncio.create_task 사용 예입니다.

import asyncio

# Define a coroutine
async def greet(name):
    await asyncio.sleep(1)  # Simulate an I/O-bound operation
    print(f"Hello, {name}!")

async def main():
    # Create tasks
    task1 = asyncio.create_task(greet("Alice"), name="GreetingAlice")
    task2 = asyncio.create_task(greet("Bob"), name="GreetingBob")

    # Check task names
    print(f"Task 1 name: {task1.get_name()}")
    print(f"Task 2 name: {task2.get_name()}")

    # Wait for both tasks to complete
    await task1
    await task2

# Run the main function
asyncio.run(main())

작업을 생성하면 다음과 같은 다양한 메서드를 실행할 수 있습니다.

  • .cancel(): 작업을 취소합니다.

  • .add_done_callback(cb): 작업 완료 시 실행되는 콜백 함수를 추가합니다.

  • .done(): 작업이 완료되었는지 확인합니다.

  • .result(): 작업이 완료된 후 작업 결과를 검색합니다.

이제 작업 생성 방법을 이해했으므로 하나의 작업 또는 여러 작업에 대한 대기를 처리하는 방법을 살펴보겠습니다.

작업 완료를 기다리는 중

이 섹션에서는 하나 이상의 작업에 대해 작업 완료를 기다리는 방법에 대해 설명합니다. 비동기 프로그래밍은 비동기 작업이 실행 중인 경우 프로그램 실행을 계속할 수 있다는 사실을 기반으로 합니다. 프로그램 실행을 안전하게 계속하기 전에 흐름을 더 잘 제어하고 작업할 수 있는 결과가 있는지 확인하고 싶은 경우가 있을 수 있습니다.

단일 작업 완료를 기다리려면 asyncio.wait_for를 사용할 수 있습니다. 두 가지 인수가 필요합니다:

  • awaitable(필수): 기다리고 싶은 코루틴, 작업 또는 future입니다. 코루틴 함수 호출, asyncio.Task 또는 asyncio.Future와 같이 기다릴 수 있는 모든 객체가 될 수 있습니다.

  • timeout(선택 사항): aw가 완료될 때까지 기다리는 최대 시간(초)을 지정합니다. 시간 초과에 도달했지만 대기 가능 항목이 완료되지 않은 경우 asyncio.wait_for는 TimeoutError를 발생시킵니다. 시간 초과가 없음으로 설정되면 함수는 대기 가능 항목이 완료될 때까지 무기한 기다립니다.

다음은 이 방법이 사용된 예입니다.

import asyncio

async def slow_task():
    print("Task started...")
    await asyncio.sleep(5)  # Simulating a long-running task
    print("Task finished!")
    return "Completed"

async def main():
    try:
        # Wait for slow_task to finish within 2 seconds
        result = await asyncio.wait_for(slow_task(), timeout=2)
        print(result)
    except asyncio.TimeoutError:
        print("The task took too long and was canceled!")

asyncio.run(main())

위 코드에서 Slow_task()는 5초 동안 휴면하여 장기 실행 작업을 시뮬레이션하는 코루틴입니다. asyncio.wait_for(slow_task(), timeout=2) 줄은 작업이 완료될 때까지 기다리지만 대기 시간을 2초로 제한하여 작업 시간이 더 오래 걸리므로 시간 초과가 발생합니다. 시간 초과가 초과되면 TimeoutError가 발생하고 작업이 취소되며 작업이 너무 오래 걸렸다는 메시지를 인쇄하여 예외를 처리합니다.

여러 작업 또는 그룹 작업이 완료될 때까지 기다릴 수도 있습니다. 이는 asyncio.wait, asyncio.gather 또는 asyncio.as_completed를 사용하여 가능합니다. 각 방법을 살펴보겠습니다.

asyncio.기다려

asyncio.wait 메소드는 작업 모음을 기다리고 두 세트(완료된 작업에 대해 하나, 보류 중인 작업에 대해 하나)를 반환합니다. 다음 인수를 사용합니다:

  • aws(필수, 반복 가능한 어웨이터블): 기다리고 싶은 코루틴 객체, 작업 또는 미래의 모음입니다.

  • timeout(float 또는 None, 선택 사항): 대기할 최대 시간(초)입니다. 제공되지 않으면 무한정 대기합니다.

  • return_when(상수, 선택): asyncio.wait가 언제 반환해야 하는지 지정합니다. 옵션은 다음과 같습니다:

    • asyncio.ALL_COMPLETED (default): Returns when all tasks are complete.
    • asyncio.FIRST_COMPLETED: Returns when the first task is completed.
    • asyncio.FIRST_EXCEPTION: Returns when the first task raises an exception.

Let's see how it is used in an example.

import asyncio
import random

async def task():
    await asyncio.sleep(random.uniform(1, 3))

async def main():
    tasks = [asyncio.create_task(task()) for _ in range(3)]
    done, pending = await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)
    print(f"Done tasks: {len(done)}, Pending tasks: {len(pending)}")

asyncio.run(main())

In the code above, asyncio.wait waits for a group of tasks and returns two sets: one with completed tasks and another with those still pending. You can control when it returns, such as after the first task is completed or after all tasks are done. In the example, asyncio.wait returns when the first task is completed, leaving the rest in the pending set.

asyncio.gather

The asyncio.gather method runs multiple awaitable objects concurrently and returns a list of their results, optionally handling exceptions. Let's see the arguments it takes.

  • *aws (required, multiple awaitables): A variable number of awaitable objects (like coroutines, tasks, or futures) to run concurrently.

  • return_exceptions (bool, optional): If True, exceptions in the tasks will be returned as part of the results list instead of being raised.

Let's see how it can be used in an example.

import asyncio
import random

async def task(id):
    await asyncio.sleep(random.uniform(1, 3))
    return f"Task {id} done"

async def main():
    results = await asyncio.gather(task(1), task(2), task(3))
    print(results)

asyncio.run(main())

In the code above, asyncio.gather runs multiple awaitable objects concurrently and returns a list of their results in the order they were passed in. It allows you to handle exceptions gracefully if return_exceptions is set to True. In the example, three tasks are run simultaneously, and their results are returned in a list once all tasks are complete.

asyncio.as_completed

The asyncio.as_completed method is used to return an iterator that yields tasks as they are completed, allowing results to be processed immediately. It takes the following arguments:

  • aws (iterable of awaitables): A collection of coroutine objects, tasks, or futures.

  • timeout (float or None, optional): The maximum number of seconds to wait for tasks to complete. If not provided, it waits indefinitely.

Example

import asyncio
import random

async def task(id):
    await asyncio.sleep(random.uniform(1, 3))
    return f"Task {id} done"

async def main():
    tasks = [task(i) for i in range(3)]
    for coro in asyncio.as_completed(tasks):
        result = await coro
        print(result)

asyncio.run(main())

In the example above, asyncio.as_completed returns an iterator that yields results as each task completes, allowing you to process them immediately. This is useful when you want to handle results as soon as they're available, rather than waiting for all tasks to finish. In the example, the tasks are run simultaneously, and their results are printed as each one finishes, in the order they complete.

So to make a summary, you use:

  • asyncio.wait: when you need to handle multiple tasks and want to track which tasks are completed and which are still pending. It's useful when you care about the status of each task separately.

  • asyncio.gather: when you want to run multiple tasks concurrently and need the results in a list, especially when the order of results matters or you need to handle exceptions gracefully.

  • asyncio.as_completed: when you want to process results as soon as each task finishes, rather than waiting for all tasks to complete. It’s useful for handling results in the order they become available.

However, these methods don't take atomic task management with built-in error handling. In the next section, we will see about asyncio.TaskGroup and how to use it to manage a group of tasks.

asyncio.TaskGroup

asyncio.TaskGroup is a context manager introduced in Python 3.11 that simplifies managing multiple tasks as a group. It ensures that if any task within the group fails, all other tasks are canceled, providing a way to handle complex task management with robust error handling. The class has one method called created_task used to create and add tasks to the task group. You pass a coroutine to this method, and it returns an asyncio.Task object that is managed by the group.

Here is an example of how it is used:

import asyncio

async def task1():
    await asyncio.sleep(1)
    return "Task 1 done"

async def task2():
    await asyncio.sleep(2)
    return "Task 2 done"

async def task_with_error():
    await asyncio.sleep(1)
    raise ValueError("An error occurred")

async def main():
    try:
        async with asyncio.TaskGroup() as tg:
            task1 = tg.create_task(task1())
            task2 = tg.create_task(task2())
            error_task = tg.create_task(task_with_error())
    except Exception as e:
        print(f"Error: {e}")

    # Print results from completed tasks
    print("Task 1 result:", task1.result())
    print("Task 2 result:", task2.result())

asyncio.run(main())

asyncio.TaskGroup manages multiple tasks and ensures that if any task fails, all other tasks in the group are canceled. In the example, a task with an error causes the entire group to be canceled, and only the results of completed tasks are printed.

Usage for this can be in web scraping. You can use asyncio.TaskGroup to handle multiple concurrent API requests and ensure that if any request fails, all other requests are canceled to avoid incomplete data.

We are at the end of the article and we have learned the multiple methods asyncio provides to create and manage tasks. Here is a summary of the methods:

  • asyncio.wait_for: Wait for a task with a timeout.

  • asyncio.wait: Wait for multiple tasks with flexible completion conditions.

  • asyncio.gather: Aggregate multiple tasks into a single awaitable.

  • asyncio.as_completed: 작업이 완료되면 처리합니다.

  • asyncio.TaskGroup: 실패 시 자동 취소로 작업 그룹을 관리합니다.

결론

비동기 프로그래밍은 Python에서 동시 작업을 처리하는 방식을 변화시켜 코드를 더 효율적이고 응답성이 뛰어나게 만들 수 있습니다. 이 기사에서는 간단한 시간 초과부터 정교한 작업 그룹까지 작업을 생성하고 관리하기 위해 asyncio에서 제공하는 다양한 방법을 살펴보았습니다. asyncio.wait_for, asyncio.wait, asyncio.gather, asyncio.as_completed 및 asyncio.TaskGroup과 같은 각 메서드를 사용하는 시기와 방법을 이해하면 비동기 프로그래밍의 잠재력을 최대한 활용하여 애플리케이션을 더욱 강력하고 확장 가능하게 만드는 데 도움이 됩니다.

비동기 프로그래밍과 보다 실용적인 예제에 대해 더 자세히 알아보려면 여기에서 자세한 가이드를 살펴보세요.

이 기사가 마음에 드셨다면 향후 업데이트를 놓치지 않도록 뉴스레터 구독을 고려해 보세요.

즐거운 코딩하세요!

위 내용은 Asyncio를 사용하여 작업 생성 및 관리의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.