본문 바로가기
Python

Python asyncio 비동기 처리 async/await

by Byeong 2025. 1. 9.

 평소 Celery를 활용한 비동기 처리를 해왔지만, 이번에는 Python에서 기본으로 지원하는 asyncio 비동기 처리에 대해 학습한 내용을 정리한다.

 

asyncio란?

asyncio는 Python에서 기본적으로 지원하는 비동기 처리 라이브러리로, 비동기 작업을 효율적으로 처리할 수 있도록 도와준다.

  • 동기 방식: 한 가지 작업이 완료된 후에야 다음 작업을 시작하는 방식.
  • 비동기 방식: 한 작업이 끝나지 않았더라도 다른 작업을 동시에 처리하는 방식으로 병렬 처리가 가능하다.

 

다음 Python코드를 통해 알아보자 


동기 방식과 비동기 방식 비교

동기 방식 

다음은 서로 다른 입력 값으로 factorial(팩토이얼) 함수를  2번 실행시켜 출력하는 코드이다. 

import time

def factorial_sync(number):
    start = time.time()
    total = 1
    for i in range(1, number+1):
        print(f'{number}! 계산 중: {total} * {i}')
        total *= i
        time.sleep(1)
    end = time.time()
    print(f'{number}! = {total} / 걸린시간 = {end-start:.3f}')

def main_sync():
    start = time.time()

    factorial_sync(3)
    factorial_sync(4)

    end = time.time()

    print(f' 총 걸린시간 = {end-start:.3f}')


if __name__ == '__main__':
    main_sync()

 

 

실행 결과

3! 계산 중: 1 * 1
3! 계산 중: 1 * 2
3! 계산 중: 2 * 3
3! = 6 / 걸린시간 = 3.015
4! 계산 중: 1 * 1
4! 계산 중: 1 * 2
4! 계산 중: 2 * 3
4! 계산 중: 6 * 4
4! = 24 / 걸린시간 = 4.016
총 걸린시간 = 7.031

 

 

 3!은 3초, 4! 은 4초로 총 걸린 시간은 7초로 팩토이얼 2개의 함수가 순차적으로 실행 한 거를 확인이 가능하다.


비동기 방식

import time
import asyncio

async def factorial_async(number):
        start = time.time()
        total = 1
        for i in range(1, number+1):
            print(f'{number}! 계산 중: {total} * {i}')
            total *= i
            await asyncio.sleep(1)
        end = time.time()
        print(f'{number}! = {total} / 걸린시간 = {end-start:.3f}')

async def main_async():
        start = time.time()

        await asyncio.wait([
                asyncio.create_task(factorial_async(3)),
                asyncio.create_task(factorial_async(4))
                ])
        
        end = time.time()
        print(f'총 걸린시간 = {end-start:.3f}')


if __name__ == '__main__':
        asyncio.run(main_async())

 

 

async

비동기 함수를 정의할 때 사용하며, 이러한 함수를 코루틴(coroutine)이라고 부른다.

 

await

 코루틴을 호출하기 위해서는 함수명 앞에 await를 작성해 줘야 한다. 코루틴 수행 중 await를 만나면 호출한 코루틴이 종료될 때까지 기다리지 않고 제어권을 메인 스레드나 다른 코루틴으로 넘긴다. 

 

asyncio.create_teask()

 코루틴을 동시에 실행하는 데 꼭 필요하며 작업(task)을 생성한다. 작업을 생성하지만, 즉시 실행되지는 않는다.

 

asyncio.wait()

 주어진 코루틴이 병렬적으로 실행하면서, 모든 작업이 끝날 때까지 대기할 수 있다.

 

asyncio.run()

 이벤트 루프를 생성하고, 지정된 코루틴을 실행한 뒤, 모든 작업이 완료되면 이벤트 루프를 종료한다.

실행 결과 

3! 계산 중: 1 * 1
4! 계산 중: 1 * 1
3! 계산 중: 1 * 2
4! 계산 중: 1 * 2
3! 계산 중: 2 * 3
4! 계산 중: 2 * 3
3! = 6 / 걸린시간 = 3.003
4! 계산 중: 6 * 4
4! = 24 / 걸린시간 = 4.004
총 걸린시간 = 4.004

 

3! 과 4! 이 교대로 호출되며 걸리는 시간도 4초로 동기 방식보다 3초가 준 것을 볼 수 있다.

 

asyncio의 핵심:이벤트 루프

이벤트 루프는 코루틴을 등록하고, 실행을 스케줄링하며, 입출력 작업 완료 시 대기 중인 코루틴을 다시 실행시킨다.

동시에 작업을 처리하는 것이 아니라, 이벤트 루프를 통해 작업을 효율적으로 스케줄링하여 처리한다. 


 마무리

 asyncio는 동기 방식보다 효율적인 리소스 사용과 빠른 작업 처리를 가능하게 하며 주로 대기 시간이 많은 작업에서 사용한다.

동기 방식과 함께 적절하게 사용하면 최적의 코드를 작성할 수 있다.