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 키워드를 중심으로 실제 애플리케이션을 구성하는 방법을 다룹니다.