[Concurrency] 이벤트 루프 패턴
이벤트 루프는 비교적 단순한 형태의 동시성 구현 패턴 중 하나다.
싱글코어나 코어가 적은 환경에서, 그렇게 대단한 병렬처리가 필요하지 않은 상황에서 적합하다.
그리고 I/O 집약적인 환경에서는 나쁘지 않은 효율을 보여준다.
방법론
기본적인 방법론 자체는 매우 단순한 편이다.
하나의 메인 루프를 하나 두고, 메세지가 들어올때마다 하나씩 순서대로 처리를 하는 것이다.
function main
initialize()
while message != quit
message := get_next_message() # 메세지가 들어옴
process_message(message) # 메세지를 처리
end while
end function
이벤트가 들어오면 그 이벤트를 적절히 처리하고, 또 다음 이벤트를 가져오고를 반복하는 것이다.
이 이벤트 루프 자체는 무한루프로서 별도의 백그라운드 스레드를 하나 사용해야 한다.
그래서 일반적으로는 다음과 같은 형태로 구현된다.
백그라운드 루프가 포그라운드와 별개로 항상 돌아야 하기 때문에, 스레드가 최소 2개는 필요하다.
포그라운드에서 뭔가 무거운 I/O 요청 같은 것을 하면 그걸 바로 하는게 아니라, 일단 이벤트 큐에 밀어넣는다.
그러면 이벤트 루프가 상황 될때마다 꺼내서 처리하고 결과를 내보내주는 것이다.
포그라운드는 이벤트를 전송하고 이벤트루프가 일을 끝내기 전까지는 다른 일을 해도 된다. 그래서 I/O 작업들을 거의 non-blocking인 것처럼 처리할 수 있다는 장점이 있다.
게다가 다른 동시성 구조와는 다르게, 스레드를 잘 활용하지는 않기 때문에 데드락 등의 문제가 적고, 컨텍스트 스위칭 같은 비싼 연산을 남발하지 않는다는 장점이 있다. 단순함에서 나온 강력함이다.
하지만 결국 멀티코어를 잘 활용할 수 있는 구조는 아니라서, CPU 집약적인 경우에는 한계가 매우 크다.
응용
언어 수준에서도 많이 쓰인다.
싱글스레드라는 제약을 갖고 있는 언어들은 기술적인 한계로 이벤트루프 기반의 동시성 기능만을 제공하는 경우가 많았다.
특히 Python의 asyncio, Node.js의 기본 동시성 기능이 전부 이벤트 루프 기반으로 구현된 것이다.
또한, GUI 환경에서도 매우 많이 응용되는 매커니즘이다. GUI 프레임워크나 라이브러리라면 이벤트 루프 구조를 거의 반드시 쓴다고 봐도 된다.
GUI 앱들은 사용자의 입력 소스가 매우 많고, 언제 어떤 입력 소스가 트리거될지도 모른다. 이걸 이벤트 기반으로 구성하지 않으면 복잡도가 너무 올라가고 리소스 낭비도 심해지기 때문이다.
게다가 전력 효율성 측면에서도 유효하다.
참조
https://en.m.wikipedia.org/wiki/Event_loop
https://notgull.net/calloop/