파이썬 비동기: 이벤트 루프의 핵심 개념

이벤트 루프란 무엇인가?

비동기 프로그래밍의 중심에는 이벤트 루프가 있다. 이는 단일 스레드 내에서 코루틴을 관리하고 실행하는 환경이다. 이벤트 루프는 비동기 작업의 흐름을 제어하며, 다음과 같은 기능을 수행한다:

  • 코루틴의 실행 및 진행
  • 콜백 함수 호출
  • 네트워크 입출력 처리 (예: 소켓 통신)
  • 서브프로세스 관리
이 패턴은 전통적인 반복 구조를 기반으로 하며, 현재는 자바스크립트 생태계에서 널리 사용되면서 더욱 주목받고 있다. 이벤트 루프는 이름 그대로 반복되는 루프로, 등록된 작업 목록(코루틴 또는 태스크)을 차례로 진행하면서, 동시에 콜백이나 입출력 작업을 처리한다. 각 반복 단계에서 가능한 작업들을 우선순위에 따라 실행하고, 대기 상태인 작업은 일시 중단한 후 다시 재개된다. Python의 asyncio 모듈은 이벤트 루프에 접근하고 조작할 수 있는 인터페이스를 제공한다. 그러나 일반 애플리케이션 개발자에게는 직접적으로 사용할 필요가 거의 없다. 대부분의 경우, 고수준의 async/await 문법만으로 충분하다. 이러한 저수준 인터페이스는 주로 라이브러리나 프레임워크 개발자가 asyncio 기반 시스템을 확장하거나 통합할 때 활용된다.

이벤트 루프 생성과 접근 방법

일반적으로 asyncio.run() 함수를 통해 이벤트 루프를 시작한다. 이 함수는 입력된 코루틴을 실행하고, 완료될 때까지 블록킹하며, 이후 루프를 정리한다.

필요 시, 직접 새로운 이벤트 루프를 생성할 수도 있다. 이를 위해 asyncio.new_event_loop() 함수를 사용할 수 있다.

import asyncio

# 새로운 이벤트 루프 생성
custom_loop = asyncio.new_event_loop()

print(custom_loop)

실행 결과 예시:

<_UnixSelectorEventLoop running=False closed=False debug=False>

이 출력은 현재 루프가 실행 중이지 않고, 닫히지도 않았으며 디버그 모드도 꺼져 있음을 의미한다.

이미 실행 중인 이벤트 루프에 접근하고 싶다면, asyncio.get_running_loop()를 사용하면 된다. 이 함수는 현재 스레드에서 활성화된 루프를 반환하며, 이외의 상황에서는 예외를 발생시킨다.

또한 asyncio.get_event_loop()라는 함수도 존재하지만, 파이썬 3.10부터는 사용이 권장되지 않으며 폐기 예정이다. 대신 위의 두 함수를 사용해야 한다.

이벤트 루프 객체의 구현 방식

이벤트 루프는 실제 객체로 구현되며, AbstractEventLoop 클래스에 의해 정의된 공통 인터페이스를 따르며, 플랫폼에 따라 다양한 구현체가 존재한다.

  • Unix 기반 시스템 (Linux, macOS): 기본적으로 SelectorEventLoop를 사용. 이는 epoll 또는 kqueue 등의 선택적 메커니즘을 기반으로 비차단 입출력을 처리한다.
  • Windows: ProactorEventLoop가 기본 설정. 이는 Windows의 I/O Completion Port 기반으로 설계되어 있으며, 비동기 파일/네트워크 입출력에 최적화되어 있다.

특정 목적을 위한 성능 최적화가 필요한 경우, 제3자 라이브러리에서도 자체 이벤트 루프를 구현할 수 있다.

왜 이벤트 루프에 접근해야 하는가?

이벤트 루프에 접근하는 이유는 다음과 같다:

  • 실행 중인 태스크의 진행 상황을 실시간으로 모니터링
  • 외부에서 코루틴을 큐잉하고 결과를 수집
  • 한 번만 실행하고 종료되는 태스크를 생성 후 무시 (데몬형 작업)

또한 이벤트 루프는 일반적인 스레드 풀보다 더 효율적인 비동기 작업 처리 기반으로 활용될 수 있다. 특히, 복잡한 비동기 로직이 포함된 어플리케이션 내부에서 이벤트 루프를 임베딩하여 유연하게 조작할 수 있다.

태그: asyncio Event Loop coroutine Non-blocking I/O Python concurrency

7월 9일 20:29에 게시됨