[Python] Coroutine 기본

Python에서 코루틴(Coroutine)은 비동기 프로그래밍을 가능하게 하는 중요한 개념입니다. 이번 글에서는 코루틴이 무엇인지, 어떻게 정의하고 호출하는지, 일반 함수와의 차이점, 그리고 CPU-bound 작업이 길어질 때 asyncio.to_thread()를 활용하는 방법까지 자세히 알아보겠습니다.


1. 코루틴이란?

코루틴은 함수의 실행을 중간에 일시 중지하고, 나중에 다시 그 지점부터 실행을 재개할 수 있는 특별한 함수입니다. 일반 함수와 달리 비동기 작업을 효율적으로 처리할 수 있게 해줍니다. Python에서는 async def 키워드를 사용해 코루틴을 정의합니다.

코루틴은 주로 I/O 바운드 작업(네트워크 요청, 파일 입출력 등)에서 CPU를 낭비하지 않고 다른 작업을 처리할 수 있게 해줍니다.


2. 코루틴 정의와 호출 방법

코루틴 정의

import asyncio

async def my_coroutine():
    print("코루틴 시작")
    await asyncio.sleep(1)  # 1초 대기 (비동기)
    print("코루틴 끝")
  • async def로 코루틴 함수를 정의합니다.
  • await 키워드는 다른 코루틴이나 비동기 작업의 완료를 기다릴 때 사용합니다.

코루틴 호출

코루틴은 일반 함수처럼 호출해도 바로 실행되지 않고, 코루틴 객체를 반환합니다. 실행하려면 이벤트 루프에서 실행해야 합니다.

async def main():
    await my_coroutine()

asyncio.run(main())
  • asyncio.run() 함수는 이벤트 루프를 생성하고, 코루틴을 실행합니다.
  • await를 통해 코루틴이 완료될 때까지 기다립니다.

3. 일반 함수와 코루틴의 차이점

구분 일반 함수 코루틴 함수
정의 방식 def async def
호출 결과 실행 결과 반환 코루틴 객체 반환
실행 방식 호출 즉시 실행 이벤트 루프에서 실행 필요
중단 가능 여부 중간에 실행 중단 불가 await로 실행 중단 가능
주 용도 동기 작업 처리 비동기 작업 처리

코루틴은 비동기 작업을 효율적으로 처리하기 위해 설계된 함수입니다. 일반 함수는 호출 즉시 실행되지만, 코루틴은 이벤트 루프에서 실행되어 다른 작업과 병렬로 처리할 수 있습니다.


4. CPU-bound 작업과 asyncio.to_thread()

asyncio는 본래 I/O 바운드 작업에 최적화되어 있습니다. 하지만 CPU를 많이 사용하는 작업(CPU-bound)은 코루틴만으로는 효율적으로 처리하기 어렵습니다. 이럴 때 asyncio.to_thread()를 사용하여 CPU-bound 작업을 별도의 스레드에서 실행할 수 있습니다.

CPU-bound 작업 예시

import asyncio
import time

def cpu_bound_task(x):
    print(f"CPU 작업 시작: {x}")
    time.sleep(3)  # CPU 작업 시뮬레이션 (블로킹)
    print(f"CPU 작업 끝: {x}")
    return x * x

async def main():
    print("비동기 작업 시작")
    result = await asyncio.to_thread(cpu_bound_task, 5)
    print(f"결과: {result}")

asyncio.run(main())
  • cpu_bound_task는 블로킹 작업입니다.
  • asyncio.to_thread()는 이 작업을 별도의 스레드에서 실행해 이벤트 루프를 막지 않습니다.
  • 여러 CPU-bound 작업이 있을 때도 비동기적으로 처리할 수 있습니다.

5. 마무리

  • 코루틴은 async def로 정의하며, await로 비동기 작업을 기다립니다.
  • 일반 함수와 달리 코루틴은 이벤트 루프에서 실행됩니다.
  • CPU-bound 작업은 asyncio.to_thread()로 별도 스레드에서 실행해 비동기 환경을 유지할 수 있습니다.

TL;DR

  • 코루틴은 async def로 정의하는 비동기 함수입니다.
  • await로 중간에 실행을 멈추고 다른 작업을 처리할 수 있습니다.
  • CPU-bound 작업은 asyncio.to_thread()로 별도 스레드에서 실행해 효율적 처리 가능.

댓글남기기