2.32. 코루틴#
코루틴(coroutine) 은 실행 도중에 일시 중단했다가 나중에 중단된 지점에서 모든 지역 변수를 그대로 유지한 채 다시 이어서 실행할 수 있는 함수입니다. 이는 제너레이터 패턴 – 일시 중단했다가 다시 이어갈 수 있는 함수 본문 – 을 그대로 따르되, 각 재개를 누가 주도하는지에서 한 가지가 달라집니다.
코루틴을 작성하는 Python 구문은 async / await 키워드 쌍입니다. async 는 해당 함수를 코루틴으로 표시하고, await 는 그 안에서 일시 중단이 허용되는 지점을 표시합니다.
2.32.1. 코루틴 정의하기#
async def 로 정의된 함수는 코루틴 함수(coroutine function) 입니다. 이 함수를 호출해도 본문이 실행되지 않으며, 아직 시작되지 않은 코루틴 객체(coroutine object) 를 반환합니다:
async def greet(name):
print("hello,", name)
coro = greet("Alice") # body NOT run yet
코루틴 객체는 함수의 맨 처음에서 일시 중단된 상태입니다. 본문을 실행하려면 무언가가 이를 주도(drive) 해야 하는데, 그 무언가가 바로 런타임 구성 요소인 이벤트 루프입니다. MicroPython은 asyncio 모듈에 이벤트 루프 하나를 기본 제공합니다. 지금은 코루틴을 “실행할 준비가 되었으며, 주도자를 기다리는 상태”로 생각하면 됩니다.
2.32.2. 코루틴 내부에서 일시 중단하기#
코루틴 내부에서 await 식은 기다리는 값이 준비될 때까지 실행을 일시 중단합니다:
async def fetch_and_log():
data = await read_sensor()
print("got:", data)
본문이 await read_sensor() 에 도달하면, 코루틴은 자신을 실행하던 쪽으로 제어를 넘깁니다. read_sensor() 가 완료되면, 주도자는 그 결과를 data 에 바인딩한 채 다음 줄에서 코루틴을 다시 이어갑니다.
await 는 코루틴 내부에서만 유효합니다. 일반 함수에서 사용하면 구문 오류입니다.
2.32.3. 제너레이터와의 관계#
코루틴과 제너레이터는 동일한 기본 메커니즘을 공유합니다. 차이는 각 재개를 누가 끌어내는가에 있습니다:
제너레이터는 값 을 내보내고(yield), 소비자가
next()로 또는 반복을 통해 다음 값을 끌어옵니다.코루틴은 제어 를 내보내고, 기다리던 작업이 준비되면 이벤트 루프가 재개를 스케줄링합니다.
제너레이터의 yield 핸드셰이크가 이해된다면, 코루틴의 핸드셰이크도 같은 개념입니다 – 단지 for 루프 대신 이벤트 루프가 주도할 뿐입니다.
이벤트 루프(event loop) 는 무언가(타이머, 네트워크 이벤트, 다른 코루틴의 완료 등)를 기다리는 코루틴 목록을 관리하는 작은 디스패처입니다. 매 반복마다 기다림이 충족된 코루틴을 골라 다음 await 까지 재개하고, 그 코루틴이 이제 무엇을 기다리는지 기록한 다음 준비된 다른 코루틴으로 넘어갑니다. 그 결과 단일 스레드에서 여러 작업이 동시에 진행됩니다 – 각 코루틴은 자신의 await 지점에서 자발적으로 제어를 양보하고, 루프는 그 빈 시간을 진행 가능한 다른 코루틴들로 채웁니다.
내부적으로 await 와 yield 는 함수를 일시 중단하고 재개하는 데에 동일한 Python 런타임 기능을 사용합니다. 키워드가 다른 이유는 그것을 둘러싼 관례가 다르기 때문입니다: yield 는 next() 로 값을 끌어오는 소비자에게 값을 돌려주고, await 는 기다리던 작업이 준비되면 재개를 스케줄링하는 이벤트 루프에 제어를 넘깁니다. async / await 는 본질적으로 코루틴 패턴을 위한 더 새로운 구문입니다 – 오래된 라이브러리들은 코루틴 간의 일시 중단을 위임하기 위해 (이터레이터와 제너레이터 에서 소개한) yield from 을 사용하여 제너레이터 기계 장치 위에 코루틴을 직접 구축했습니다.
2.32.4. 코루틴에는 주도자가 필요하다#
코루틴은 이를 주도할 런타임이 없으면 작동하지 않습니다. 정의하는 것은 괜찮지만, 실행하려면 이벤트 루프가 필요합니다. MicroPython의 asyncio 모듈이 그 이벤트 루프를 제공합니다. Asyncio 섹션에서는 루프를 시작하는 방법, 코루틴을 루프에 스케줄링하는 방법, 락과 이벤트로 코루틴 간에 상태를 공유하는 방법, 취소와 타임아웃을 처리하는 방법, 그리고 여기서 소개한 async / await 키워드를 중심으로 실제 애플리케이션을 구성하는 방법을 다룹니다.