8.7. 이벤트

asyncio.Event는 모듈이 제공하는 가장 단순한 신호 프리미티브입니다. 한 코루틴이 어떤 일이 일어났을 때 이벤트를 설정하고, 다른 임의 개수의 코루틴이 계속 진행하기 전에 이벤트가 설정되기를 기다립니다. 페이로드는 없습니다 – 이벤트는 켜졌다가 무언가가 지울 때까지 켜진 상태로 유지되는 불리언입니다.

8.7.1. 기본 형태

import asyncio

async def waiter(evt):
    print("waiting")
    await evt.wait()
    print("got signal")

async def main():
    evt = asyncio.Event()
    asyncio.create_task(waiter(evt))
    await asyncio.sleep(1)
    evt.set()
    await asyncio.sleep(0)

asyncio.run(main())

대기자 태스크가 실행되어 await evt.wait()에 도달하고 일시 중단됩니다 – 이벤트는 지워진 상태로 시작하므로 wait 호출이 루프로 제어권을 양보합니다. 1초 후 mainevt.set()을 호출하면 대기자가 재개되도록 스케줄링됩니다. 끝에 있는 await asyncio.sleep(0)은 단지 main이 반환하기 전에 루프가 대기자를 실행할 기회를 갖도록 하는 yield입니다.

8.7.2. 네 가지 메서드

  • set() – 이벤트를 설정 상태로 바꾸고, 현재 wait()에서 차단된 모든 코루틴이 재개되도록 스케줄링합니다. 이벤트는 지워질 때까지 설정 상태로 유지됩니다.

  • clear() – 이벤트를 다시 지워진 상태로 바꿉니다. 이후에 await wait()하는 코루틴은 다시 차단됩니다.

  • wait() – 코루틴입니다. 이벤트가 이미 설정되어 있으면 즉시 반환합니다. 그렇지 않으면 무언가가 설정할 때까지 차단합니다.

  • is_set() – 차단 없이 현재 상태를 반환합니다. 코루틴이 이벤트를 기다리지 않고 확인하려 할 때 유용합니다.

8.7.3. 여러 대기자

여러 코루틴이 동일한 이벤트를 await할 수 있습니다. 무언가가 set()을 호출하면 그들 모두가 재개되도록 스케줄링됩니다. 이벤트는 기본적으로 일대다이므로, 여러 대기자에게 분산하기 위해 여러 개의 이벤트가 필요하지 않습니다.

8.7.4. 이벤트 재사용

흔한 패턴은 이벤트를 반복적으로 사용하는 것입니다 – 신호마다 한 번씩 실행되는 코루틴 – 매번 통과한 후 이벤트를 지움으로써 가능합니다:

async def worker(go):
    while True:
        await go.wait()
        do_one_unit_of_work()
        go.clear()

생산자는 작업이 있을 때마다 이벤트를 설정하고, 작업자는 신호를 받아들이면 이를 지웁니다. 작업자가 깨어나기 전에 생산자가 이벤트를 다시 설정할 수 있다면, 작업자는 지우기 전에 그 경우를 처리하고 싶을 수 있습니다(while go.is_set(): ...).

8.7.5. 안전한 컨텍스트

Event는 인터럽트 핸들러 내부에서 set()을 호출하기에 안전하지 않습니다. 대기자를 스케줄링하는 데 사용하는 메커니즘은 자신이 이벤트 루프 자체의 컨텍스트 안에서 실행된다고 가정하는데, 인터럽트는 그렇지 않습니다. 인터럽트 핸들러에서 asyncio 태스크를 깨우려면 ThreadSafeFlag(곧 다룸)가 적합한 프리미티브입니다.