8.3. 이벤트 루프¶
이벤트 루프는 asyncio가 그 아래에서 실행하는 엔진입니다. 이것은 프로그램의 모든 태스크 목록을 유지하고, 각 태스크에게 그 다음 await 까지 실행하도록 요청한 다음, 다음으로 준비된 태스크로 넘어갑니다. 준비된 태스크가 없으면 대기하며 — 이 실제 대기가 펌웨어가 다른 일을 실행하고 절전 슬립이 작동할 수 있도록 CPU를 사용 가능하게 만드는 것입니다 — 태스크가 await하던 무언가가 사용 가능해질 때까지 기다린 다음 그 태스크를 재개합니다. 이것을 영원히 반복합니다.
대부분의 애플리케이션은 루프와 직접 상호작용하지 않습니다. 루프는 asyncio.run() 을 호출한 결과이며, 애플리케이션은 코루틴을 작성하고 그것들을 태스크로 스케줄링하면 루프가 나머지를 처리합니다.
8.3.1. asyncio.run() 이 실제로 하는 일¶
단일 호출:
asyncio.run(main())
은 루프가 애플리케이션을 대신하여 관리하는 더 긴 시퀀스의 축약형입니다:
이벤트 루프가 아직 존재하지 않으면 생성합니다.
제공된 코루틴을 태스크로 감싸고 루프의 최상위 진입점으로 스케줄링합니다.
루프를 실행합니다 — 준비된 태스크를 단계별로 진행하고, 준비된 것이 없으면 대기하며, await가 완료되면 태스크를 재개합니다 — 최상위 태스크가 반환되거나 예외를 발생시킬 때까지.
애플리케이션이 생성한 태스크 중 여전히 실행 중인 것을 취소합니다.
최상위 코루틴이 반환한 값을 반환합니다(또는 발생시킨 예외를 다시 발생시킵니다).
8.3.2. 프로그램당 단일 루프¶
MicroPython의 asyncio에는 이벤트 루프가 하나 뿐이며, 그게 전부입니다. 새 루프를 생성하는 일도 없고, 한 루프를 다른 루프 안에 중첩하는 일도 없습니다. 이미 루프에서 실행 중인 코루틴 내부에서 asyncio.run() 을 호출하는 것은 오류입니다. 루프는 이미 거기 있으며, 코루틴은 그저 시작하려던 것을 await하기만 하면 됩니다.
실제로 규칙은 이전 페이지의 마지막 줄과 동일합니다: 프로그램당 정확히 하나의 asyncio.run() 호출이 맨 위에 있고, 그 뒤에 단일 async def main() 이 있습니다. 그 외 모든 것은 main 내부에 존재합니다.
8.3.3. 직접 루프 접근¶
애플리케이션이 루프 자체를 다뤄야 하는 드문 경우 — 주로 진단 및 예외 핸들러 — 에는 asyncio.get_event_loop() 가 Loop 객체를 반환합니다. 거기서 애플리케이션은 사용자 정의 예외 핸들러를 설치하거나, 루프가 무엇을 하고 있는지 검사하거나, (아주 가끔) asyncio.create_task() 대신 create_task() 를 직접 호출할 수 있습니다(둘은 동일한 연산입니다).
Loop 이 노출하는 메서드의 전체 집합 — run_forever(), stop(), set_exception_handler() 등 — 은 이 섹션 후반부의 루프 제어 페이지에서 다룹니다. 그때까지는 asyncio.run(main()) 이 애플리케이션에 필요한 전부입니다.