asyncio의 핵심 장점 중 하나는 다수의 코루틴을 동시에 실행할 수 있다는 점입니다. 이러한 코루틴들은 하나의 그룹으로 구성되어 저장된 후 병렬적으로 실행될 수 있으며, 이는 asyncio.gather() 함수를 통해 구현됩니다.
asyncio.gather() 함수 이해
asyncio.gather() 함수는 여러 awaitable 객체들을 하나로 묶어주는 유틸리티입니다. 이를 통해 여러 작업이나 코루틴을 동시에 실행하고 결과를 기다릴 수 있습니다.
# 여러 awaitable 객체 실행 예시
결과들 = await asyncio.gather(작업1(), asyncio.create_task(작업2()))
여러 유사한 작업들을 미리 생성해두고 한 번에 실행해야 하는 상황에서 매우 유용합니다. 예를 들어 서로 다른 데이터를 처리하는 동일한 작업들이 있을 때, 이들을 그룹화하여 동시 실행할 수 있습니다.
이 함수는 단순히 작업들을 기다리는 것보다 더 강력한 기능을 제공합니다:
- await 표현식을 통해 그룹 내 모든 awaitable 객체가 완료될 때까지 대기
- 그룹화된 모든 awaitable 객체의 결과를 수집하고 result() 메서드로 조회 가능
- cancel() 메서드를 통해 그룹 전체를 취소할 수 있음
- done() 메서드로 그룹 내 모든 작업의 완료 여부 확인
- 그룹 내 모든 작업이 완료되었을 때만 콜백 함수 실행
asyncio.gather() 사용 방법
asyncio.gather() 함수는 하나 이상의 awaitable 객체를 인자로 받습니다. 여기서 awaitable 객체란 코루틴, Future, Task 등을 의미합니다.
# 여러 코루틴 실행
asyncio.gather(코루틴1(), 코루틴2())
Task 객체가 전달되면 이미 실행 중인 상태입니다. gather() 함수는 위치 인자로 awaitable 객체들을 받습니다.
# awaitable 객체 리스트 직접 전달 불가
asyncio.gather([코루틴1(), 코루틴2()]) # 오류 발생
리스트 형태로 전달하려면 언패킹 연산자(*)를 사용해야 합니다:
# 언패킹을 통한 리스트 전달
asyncio.gather(*[코루틴1(), 코루틴2()])
코루틴이 전달되면 자동으로 Task 객체로 래핑됩니다. gather() 함수는 블로킹되지 않으며, 대신 awaitable 객체 그룹을 나타내는 asyncio.Future 객체를 반환합니다.
# 여러 awaitable 객체를 나타내는 Future 얻기
그룹 = asyncio.gather(코루틴1(), 코루틴2())
Future 객체가 생성되면 이벤트 루프에서 자동으로 스케줄링됩니다. 호출자가 아무것도 하지 않아도 그룹 내 모든 awaitable 객체가 실행됩니다.
# Future 객체 대기 없이도 실행 가능
그룹 = asyncio.gather(코루틴1(), 코루틴2())
# 일시정지 및 대기, 그룹은 실행 중일 수 있음
await asyncio.sleep(10)
반환된 Future 객체를 await하면 그룹 내 모든 awaitable 객체가 완료될 때까지 기다립니다:
# awaitable 객체 그룹 실행
await 그룹
await를 통해 Future 객체를 기다리면 각 awaitable 객체의 반환값들을 리스트 형태로 받습니다:
# 그룹 실행 및 결과값 받기
결과목록 = await 그룹
보통은 한 줄로 처리합니다:
# 한 줄로 작업 실행 및 결과 받기
결과목록 = await asyncio.gather(코루틴1(), 코루틴2())
리스트로 준비된 여러 코루틴 실행 예제
여러 코루틴을 미리 생성한 후 gather()로 실행하는 것이 일반적입니다. 이는 프로그램이 동시 실행할 작업들을 준비하고 즉시 실행하며 완료를 기다릴 수 있게 해줍니다.
# 여러 코루틴 생성
코루틴들 = [태스크_코루틴(i) for i in range(10)]
코루틴 리스트는 직접 gather() 함수에 전달할 수 없으므로 언패킹 연산자를 사용해야 합니다:
# 코루틴들 실행
await asyncio.gather(*코루틴들)
전체 예제는 다음과 같습니다:
import asyncio
# 태스크용 코루틴
async def 태스크_코루틴(값):
# 메시지 출력
print(f'>태스크 {값} 실행중')
# 잠시 대기
await asyncio.sleep(1)
# 진입점 코루틴
async def 메인():
# 시작 메시지
print('메인 시작')
# 여러 코루틴 생성
코루틴들 = [태스크_코루틴(i) for i in range(10)]
# 태스크들 실행
await asyncio.gather(*코루틴들)
# 완료 메시지
print('메인 완료')
# asyncio 프로그램 시작
asyncio.run(메인())
실행 결과는 다음과 같습니다:
메인 시작
>태스크 0 실행중
>태스크 1 실행중
>태스크 2 실행중
>태스크 3 실행중
>태스크 4 실행중
>태스크 5 실행중
>태스크 6 실행중
>태스크 7 실행중
>태스크 8 실행중
>태스크 9 실행중
메인 완료
이 예제는 메인 코루틴이 10개의 코루틴 객체를 리스트 컴프리헨션으로 생성하고, 이를 gather() 함수에 언패킹하여 전달하는 과정을 보여줍니다. 모든 코루틴이 동시에 실행되며 각자의 메시지를 출력하고 종료됩니다.