728x90
반응형
Proactor 디자인 패턴: 스레드 없는 동시 실행 (The Proactor Design Pattern: Concurrency Without Threads)
Boost.Asio 라이브러리는 동기식과 비동기식 작업에 대한 지원을 함께 제공한다. 비동기식 지원은 Proactor 디자인 패턴 [POSA2]을 기반으로 한다. 전용-동기식이나 Reactor 접근 방식과 비교하는 경우, 이 접근 방식의 장점과 단점은 아래에 요약되어 있다.
Proactor와 Boost.Asio
플랫폼 세부사항을 참조하지 않고, Boost.Asio에서 Proactor 디자인 패턴이 어떻게 구현되는지 살펴본다.
Proactor 디자인 패턴 ([POSA2]에서 수정됨)
- 비동기식 작업 (Asynchronous Operation)
소켓에서 비동기 읽기/쓰기와 같은 비동기식으로 실행되는 작업을 정의한다. - 비동기식 작업 프로세서 (Asynchronous Operation Processor)
비동기식 작업을 실행하고 작업이 완료된 경우, 완료 이벤트 큐에 이벤트를 추가한다. 높은 수준의 관점에서 보면, reactive_socket_service와 같은 내부 서비스는 비동기식 작업 프로세서이다. - 완료 이벤트 큐 (Completion Event Queue)
비동기식 이벤트 디멀티플렉서로 큐에서 제거될 때까지 완료 이벤트를 큐(buffer)에 보관한다. - 완료 핸들러 (Completion Handler)
비동기 작업의 결과를 처리한다. 이들은 흔히 boost::bind를 사용하여 생성한 함수 개체이다. - 비동기식 이벤트 디멀티플렉서 (Asynchronous Event Demultiplexer)
완료 이벤트 큐에서 이벤트가 발생할 때까지 블럭 대기하고 완료된 이벤트를 호출자에게 반환한다. - Proactor
비동기식 이벤트 디멀티플렉서를 호출하여 이벤트를 큐에서 꺼내서 이벤트와 관련된 완료 핸들러(즉, 함수 개체를 호출)를 처리한다. 이 추상화는 io_context 클래스로 표현된다. - 개시자 (Initiator)
비동기식 작업을 시작하는 응용 프로그램별 코드이다. 개시자는 basic_stream_socket과 같은 고급 인터페이스를 통해 비동기식 작업 프로세서와 상호 작용하며, 이를 통해 react_socket_service와 같은 서비스에 위임된다.
Reactor를 이용한 구현
많은 플랫폼에서, Boost.Asio는 select, epoll 또는 kqueue와 같은 Reactor 관점에서 Proactor 디자인 패턴을 구현한다. 이 구현 방식은 다음과 같은 Proactor 디자인 패턴에 해당한다:
- 비동기식 작업 프로세서 (Asynchronous Operation Processor)
Reactor는 select, epoll 또는 kqueue를 사용하여 구현 된다. Reactor가 리소스 작업을 수행할 준비가 되었다고 표시하면, 프로세서는 비동기 작업을 실행하고 관련된 완료 핸들러를 완료 이벤트 큐에 추가한다. - 완료 이벤트 큐 (Completion Event Queue)
완료 핸들러(즉, 함수 개체)의 링크드 리스트이다. - 비동기식 이벤트 디멀티플렉서 (Asynchronous Event Demultiplexer)
이는 완료 이벤트 큐에서 완료 핸들러가 사용할 수 있을 때까지, 이벤트나 조건 변수를 대기하는 것으로 구현된다.
Windows Overlapped I/O를 사용한 구현
Windows NT, 2000 및 XP에서 Boost.Asio는 오버랩된 I/O를 활용하여 Proactor 디자인 패턴을 효율적으로 구현한다. 이 구현 방식은 다음과 같은 Proactor 디자인 패턴에 해당한다.
- 비동기식 작업 프로세서 (Asynchronous Operation Processor)
이는 운영 체제에서 구현된다. AcceptEx와 같은 오버랩된 함수 호출로 작업이 시작된다. - 완료 이벤트 큐 (Completion Event Queue)
이는 운영 체제에서 구현되고 I/O 완료 포트와 연관된다. io_context 인스턴스마다 하나의 I/O 완료 포트가 있다. - 비동기식 이벤트 디멀티플렉서 (Asynchronous Event Demultiplexer)
Boost.Asio에서 이벤트 및 이벤트와 연관된 완료 핸들러를 큐에서 빼려고 호출한다.
장점
- 이식성
많은 운영체제들은 고성능의 네트워크 응용프로그램 개발에 필요한 선호하는 선택 사항으로 기본(native) 비동기 I/O API(예로 Windows에서 오버랩된 I/O)를 제공한다. 라이브러리는 기본(native) 비동기 I/O 관점에서 구현될 수 있다. 그러나 기본(native) 지원이 불가능한 경우, POSIX select()와 같은 Reactor 패턴을 나타내는 동기식 이벤트 디멀티플렉서를 사용하여 라이브러리가 구현될 수도 있다. - 동시 실행에서 스레드 분리
오랜 기간 작업은 응용프로그램을 대신하여 구현물에 의해 비동기식으로 수행된다. 결과적으로 응용프로그램은 동시 실행을 늘리기 위해 많은 스레드를 생성할 필요가 없다. - 성능과 확장성
연결당 스레드(전용-동기식 접근 방식이 필요함)와 같은 구현 전략은 CPU 간의 컨텍스트 전환, 동기화 및 데이터 이동의 증가로 시스템 성능이 저하될 수 있다. 비동기식 작업을 사용하면 일반적으로 제한된 리소스인 운영 체제 스레드 수를 최소화하고 처리할 이벤트가 있는 논리적 스레드의 제어만 활성화하여 컨텍스트 전환 비용을 피할 수 있다. - 응용프로그램 동기화의 단순화
비동기식 작업 완료 핸들러는 단일 스레드 환경에 있는 것처럼 작성할 수 있으며, 동기화 문제에 대해 거의 또는 전혀 걱정하지 않고 응용프로그램 로직을 개발할 수 있다. - 함수 구성
함수 구성은 특정 형식의 메세지를 보내는 것과 같은 높은 수준의 작업을 제공하는 함수의 구현을 참조한다. 각 함수는 하위 수준의 읽기/쓰기 작업에 대해 다중 호출의 관점에서 구현된다.
예를 들어, 각 메세지가 고정 길이 헤더와 가변 길이 본문으로 구성되는 프로토콜을 고려한다. 여기서 본문의 길이가 헤더에 지정된다. 가상의 read_message 작업은 두 개의 하위 수준 읽기를 사용하여 구현될 수 있다. 첫 번째는 헤더를 수신하여 본문의 길이를 알아내고 두 번째 본문을 수신한다.
비동기식 모델에서는 함수를 구성하기 위해, 비동기식 작업을 함께 체인으로 묶을 수 있다. 즉 한 작업에 대한 완료 핸들러가 다음 작업을 시작할 수 있다. 체인에서 첫 번째 호출을 시작하면 호출자가 상위 수준 작업이 비동기식 작업 체인으로 구현된 것을 알 필요가 없도록 캡슐화할 수 있다.
이러한 방식으로 새로운 작업을 구성할 수 있는 기능은 특정 프로토콜을 지원하는 네트워크 라이브러리보다 높은 수준의 추상화 개발을 단순화 한다.
단점
- 프로그램 복잡성
작업의 시작과 종료 사이에 시공간이 분리되어 있어서, 비동기식 메커니즘을 사용하여 응용프로그램을 개발하는 것은 더 어렵다. 또한 반전된 제어 흐름으로 응용프로그램 디버깅이 더 어려울 수 있다. - 메모리 사용량
버퍼 공간은 읽기/쓰기 작업 동안 커밋되어야 하며, 이 작업은 무한정 지속될 수 있고, 각각의 동시에 실행되는 작업은 별도의 버퍼가 필요하다. 반면 Reactor 패턴은 소켓이 읽기/쓰기 준비가 될 때까지 버퍼 공간이 필요하지 않다.
참조
[POSA2] D.Schmidt et al, Pattern Oriented Software Architecture, Volume 2. Wiley, 2000.
728x90
반응형
'Boost C++ Libraries > Boost.Asio' 카테고리의 다른 글
Boost.Asio 개요 - 핵심 개념 및 기능 - 스트랜드: 명시적 잠금없이 스레드 사용 (0) | 2020.12.07 |
---|---|
Boost.Asio 개요 - 핵심 개념 및 기능 - 스레드와 Boost.Asio (0) | 2020.12.07 |
Boost.Asio 개요 - 핵심 개념 및 기능 - Boost.Asio 기본 구조 (0) | 2020.12.05 |
Boost.Asio 개요 - 핵심 개념 및 기능 (0) | 2020.12.05 |
Boost.Asio 개요 - 이론적 해석 (0) | 2020.12.04 |