비동기(Asynchronous) 이벤트 처리의 개념

비동기(Asynchronous) 이벤트 처리의 개념

1. 서론: 실시간 분산 시스템과 이벤트 중심 아키텍처의 필요성

현대의 복잡한 분산 시스템, 특히 국방, 항공우주, 자율주행, 산업 자동화와 같은 미션 크리티컬(Mission-Critical) 환경에서 데이터 미들웨어의 역할은 단순히 패킷을 전달하는 통로(Pipe)의 기능을 넘어선다. 이러한 시스템의 핵심 요구사항은 데이터의 발생과 상태 변화를 실시간으로 감지하고, 이에 즉각적으로 반응하는 것이다. 데이터 분산 서비스(DDS: Data Distribution Service)는 이러한 요구를 충족시키기 위해 데이터 중심의 통신 모델(Data-Centric Publish-Subscribe, DCPS)을 채택하고 있으며, 그 구현의 중심에는 애플리케이션이 데이터와 시스템의 상태 변화를 인지하는 두 가지 상반된 접근 방식이 존재한다. 하나는 애플리케이션이 주도적으로 상태를 확인하는 동기식(Synchronous) 폴링 모델인 WaitSetCondition이고, 다른 하나는 미들웨어가 주도권을 가지고 애플리케이션을 호출하는 비동기식(Asynchronous) 모델인 **리스너(Listener)**이다.1

본 장에서 다루는 리스너(Listener)는 DDS 표준이 정의하는 가장 직관적이면서도 강력한 이벤트 처리 메커니즘이다. 이는 소프트웨어 공학적으로 옵저버 패턴(Observer Pattern)을 네트워크 미들웨어 레벨로 확장한 것으로 볼 수 있으며, 시스템 내부에서 발생하는 수많은 상태 변화—데이터의 도착, 네트워크 연결의 성립 및 단절, 품질 정책(QoS)의 위반 등—를 애플리케이션 로직과 결합시키는 접착제(Glue) 역할을 수행한다.3 리스너를 이해한다는 것은 단순히 데이터를 읽는 API를 익히는 것을 넘어, 제어의 흐름(Control Flow)이 애플리케이션에서 미들웨어로, 다시 미들웨어에서 애플리케이션으로 넘어가는 ’제어의 역전(Inversion of Control)’을 이해하는 과정이다. 본 절에서는 리스너의 개념적 토대부터 시작하여, 내부 동작 원리, 상태 전파(Propagation)의 계층적 구조, 그리고 동기식 모델과의 심층적인 비교 분석을 통해 비동기 이벤트 처리의 본질을 파헤친다.

2. 폴링(Polling) 대 인터럽트(Interrupt): 아키텍처적 메타포

비동기 이벤트 처리의 개념을 명확히 이해하기 위해서는 컴퓨터 아키텍처의 가장 기초적인 입출력(I/O) 처리 방식인 폴링(Polling)과 인터럽트(Interrupt)의 비유를 살펴보는 것이 유효하다.5 DDS의 데이터 수신 모델은 이 하드웨어 레벨의 메커니즘을 소프트웨어 아키텍처로 그대로 옮겨온 것과 유사한 구조를 가진다.

2.1 폴링(Polling) 모델의 한계와 특성

전통적인 소켓 프로그래밍이나 절차적 시스템에서 흔히 볼 수 있는 폴링 방식은 CPU(여기서는 애플리케이션 스레드)가 주변 장치(여기서는 미들웨어)의 상태 레지스터를 주기적으로 읽어 “데이터가 준비되었는가?“를 확인하는 구조다.6

“지금 데이터가 왔는가? (아니오)”, “지금은 왔는가? (아니오)”, “지금은? (예)“와 같은 루프(Loop) 구조는 구현이 직관적이고 제어의 흐름이 명확하다는 장점이 있다.7 DDS의 WaitSet이나 Condition을 이용한 읽기 방식이 이에 해당한다.

그러나 이 방식은 본질적인 딜레마를 안고 있다. 폴링 주기를 짧게 설정하면 CPU 자원을 낭비하게 되고(Busy Waiting), 주기를 길게 설정하면 데이터가 도착한 시점과 이를 확인하는 시점 사이에 불가피한 지연(Latency)이 발생한다. 또한, 데이터가 드물게 발생하는 시스템에서는 대부분의 CPU 사이클이 의미 없는 상태 확인에 소모되는 비효율이 발생한다.6

2.2 인터럽트(Interrupt)와 리스너(Listener)의 유사성

반면, 리스너 모델은 하드웨어 인터럽트 처리 방식에 비유된다. 우리가 일상생활에서 문자 메시지를 기다릴 때, 끊임없이 휴대폰 화면을 켜서 확인하는 것(폴링)이 아니라, 주머니에 넣어두고 있다가 진동(인터럽트)이 울릴 때만 확인하는 것과 같다.5

컴퓨터 시스템에서 주변 장치가 CPU에 전기적 신호(IRQ)를 보내면, CPU는 수행 중이던 메인 루틴을 즉시 중단하고 미리 등록된 인터럽트 서비스 루틴(ISR: Interrupt Service Routine)으로 점프하여 필요한 처리를 수행한 후 다시 원래 작업으로 복귀한다.

DDS의 리스너는 바로 이 ISR에 해당한다. 미들웨어는 백그라운드에서 네트워크 패킷을 수신하고 프로토콜을 처리하다가, 유효한 데이터가 조립되거나 상태 변화가 감지되는 순간, 애플리케이션이 미리 등록해 둔 Listener 객체의 콜백 메서드(Callback Method)를 호출한다.8 이 과정은 애플리케이션의 메인 루프와는 독립적으로(비동기적으로) 발생하며, 데이터가 도착하는 즉시 처리가 시작되므로 이론적으로 가장 낮은 지연 시간(Minimal Latency)을 보장할 수 있다.9

이러한 비동기적 특성은 “할리우드 원칙(Hollywood Principle: Don’t call us, we’ll call you)“으로도 불리며, 이벤트 중심 아키텍처(EDA)를 구현하는 핵심 철학이 된다. 애플리케이션은 더 이상 데이터의 도착 여부를 신경 쓰지 않고 자신의 고유 연산에 집중할 수 있으며, 데이터 처리는 오직 이벤트가 발생했을 때만 수행되므로 CPU 효율성을 극대화할 수 있다.2

3. DDS 리스너의 작동 원리와 제어의 역전

DDS 리스너 모델의 기술적 실체는 객체 지향 디자인 패턴인 옵저버 패턴(Observer Pattern)의 정교한 구현이다. DDS의 모든 통신 객체, 즉 엔티티(Entity)는 관찰 대상(Subject)이 되며, 사용자가 구현하여 등록하는 리스너(Listener)는 관찰자(Observer)가 된다.4

3.1 제어 흐름의 역전 (Inversion of Control)

일반적인 라이브러리 사용 모델에서는 애플리케이션 코드가 라이브러리의 함수를 호출한다(Call). 예를 들어 socket.read()를 호출하여 데이터를 가져온다. 그러나 리스너 모델에서는 미들웨어 엔진이 애플리케이션의 코드를 호출한다(Callback). 이는 제어권이 애플리케이션에서 미들웨어로 넘어갔음을 의미한다.

이러한 제어의 역전은 시스템 설계에 중대한 영향을 미친다.

  1. 반응성(Responsiveness): 시스템은 외부 자극(데이터 도착)에 즉각적으로 반응하는 구조가 된다.
  2. 비결정성(Nondeterminism): 애플리케이션 입장에서 리스너 코드가 ‘언제’ 실행될지 정확히 예측할 수 없다. 이는 전적으로 네트워크 상황과 데이터 발생 시점에 의존한다. 따라서 리스너 내부의 로직은 시점에 구애받지 않는 독립적인 코드(Reentrant Code)여야 하며, 공유 자원에 접근할 때는 엄격한 동기화(Synchronization)가 필요하다.10

3.2 리스너 인터페이스와 가상 함수

DDS 표준(OMG DDS Specification)은 각 엔티티 타입별로 대응되는 리스너 인터페이스를 정의하고 있다. 예를 들어, DataReader에는 DataReaderListener, DataWriter에는 DataWriterListener가 존재한다. 이들은 모두 최상위 Listener 인터페이스를 상속받는 구조로 되어 있다.3

사용자는 이 인터페이스를 상속받아(C++의 경우) 또는 구현하여(Java의 경우) 가상 함수(Virtual Function)들을 재정의(Override)한다. 예를 들어, 데이터가 도착했을 때 호출되는 on_data_available() 함수를 구현하여 그 안에 데이터 수신 및 처리 로직을 작성한다. 그리고 이렇게 생성된 객체의 포인터를 Entity.set_listener() 메서드를 통해 미들웨어에 등록한다.11

4. DDS 통신 상태(Communication Status)의 심층 분석

리스너 모델에서 ’이벤트’라고 칭하는 것은 DDS 사양서에 정의된 **통신 상태(Communication Status)**의 변화를 의미한다. DDS는 데이터 통신 과정에서 발생할 수 있는 거의 모든 상황을 상태(Status)로 모델링하고 있으며, 각 상태는 StatusKind라는 비트 플래그로 식별된다.12

개발자는 각 상태의 의미를 정확히 이해해야 적절한 핸들러를 구현할 수 있다. 통신 상태는 크게 데이터 처리 관련 상태, 네트워크 및 연결 상태, 그리고 QoS 위반 상태로 분류할 수 있다.

4.1 데이터 가용성 상태군 (Data Availability)

데이터의 흐름과 직접적으로 관련된 가장 빈번한 이벤트들이다.

상태 이름 (Status Name)설명 및 발생 조건관련 리스너 메서드비트 마스크 값
DATA_AVAILABLE_STATUSDataReader의 수신 큐(History Cache)에 새로운 데이터 샘플이 도착하여 애플리케이션이 읽을 수 있는 상태가 되었음을 알린다. 가장 기본적이고 빈번하게 사용되는 이벤트이다.on_data_available0x0001 << 10
DATA_ON_READERS_STATUSSubscriber 레벨의 이벤트로, 해당 Subscriber가 소유한 하나 이상의 DataReader에 새로운 데이터가 도착했음을 알린다. 여러 토픽의 데이터를 통합적으로 처리하거나, 데이터 처리의 순서를 제어해야 할 때 유용하다.on_data_on_readers0x0001 << 9
SAMPLE_LOST_STATUS네트워크 패킷 손실이 복구 불가능하거나, 수신 버퍼(Resource Limit) 부족으로 인해 큐에 들어오기 전에 샘플이 유실되었음을 알린다. 데이터의 연속성이 깨졌음을 의미한다.on_sample_lost0x0001 << 7
SAMPLE_REJECTED_STATUS데이터가 정상적으로 수신되었으나, DataReader의 내부 정책(예: RESOURCE_LIMITS QoS의 max_samples 초과)에 의해 큐에 저장되지 못하고 폐기되었음을 알린다. 이는 시스템의 처리 용량이 데이터 유입 속도를 따라가지 못함을 시사한다.on_sample_rejected0x0001 << 8

12

4.2 인프라 및 연결 상태군 (Infrastructure & Liveliness)

분산 시스템의 구성 요소 간 연결 상태를 모니터링한다. DDS의 강력한 기능 중 하나인 동적 발견(Dynamic Discovery)의 완료 여부를 판단하는 데 사용된다.

상태 이름 (Status Name)설명 및 발생 조건관련 리스너 메서드비트 마스크 값
SUBSCRIPTION_MATCHED_STATUSDataReader가 자신과 동일한 토픽 및 호환되는 QoS를 가진 DataWriter를 발견(매칭)했거나, 기존 연결이 끊어졌을 때 발생한다. total_countcurrent_count를 통해 현재 연결된 퍼블리셔의 수를 파악할 수 있다.on_subscription_matched0x0001 << 11
PUBLICATION_MATCHED_STATUSDataWriter 입장에서 호환되는 DataReader와 매칭되거나 해제되었을 때 발생한다. 데이터 발행을 시작하기 전에 구독자가 있는지 확인하는 용도로 사용된다.on_publication_matched0x0001 << 13
LIVELINESS_CHANGED_STATUSDataReader 입장에서 연결된 DataWriter의 활성(Liveliness) 상태가 변경되었음을 알린다. DataWriter가 정해진 주기(lease_duration) 내에 신호를 보내지 않아 ’죽음(Not Alive)’으로 간주되거나, 다시 살아났을 때 호출된다.on_liveliness_changed0x0001 << 12
LIVELINESS_LOST_STATUSDataWriter 입장에서 자신이 약속한 LIVELINESS QoS 주기를 지키지 못해, 구독자들에게 ’죽은 것’으로 간주될 위험이 있다는 경고를 알린다.on_liveliness_lost0x0001 << 11 (Note: IDL definition varies slightly per vendor, refer to specific IDL)

12

4.3 QoS 위반 및 오류 상태군 (QoS Violation)

시스템 설계자가 설정한 통신 품질 정책이 물리적인 제약이나 시스템 오류로 인해 만족되지 못할 때 발생하는 경고성 이벤트이다.

상태 이름 (Status Name)설명 및 발생 조건관련 리스너 메서드
REQUESTED_DEADLINE_MISSEDDataReaderDEADLINE QoS 정책에 명시된 시간 주기 내에 새로운 데이터 샘플을 수신하지 못했을 때 발생한다. 실시간 제어 루프에서 데이터가 제때 도착하지 않았음을 감지하는 핵심 메커니즘이다.on_requested_deadline_missed
OFFERED_DEADLINE_MISSEDDataWriterDEADLINE QoS 정책에 명시된 주기 내에 데이터를 쓰지(Write) 못했을 때 발생한다. 애플리케이션 로직의 지연을 감지하는 데 사용된다.on_offered_deadline_missed
REQUESTED_INCOMPATIBLE_QOSDataReader가 토픽 이름이 같은 DataWriter를 발견했으나, QoS 정책이 호환되지 않아(예: Writer는 VOLATILE인데 Reader는 TRANSIENT_LOCAL을 요구함) 연결이 거부되었을 때 발생한다.on_requested_incompatible_qos
OFFERED_INCOMPATIBLE_QOSDataWriter 입장에서, 자신의 QoS가 구독자의 요구사항을 충족시키지 못해 연결이 성립되지 않았음을 알린다.on_offered_incompatible_qos

4

5. 리스너 계층 구조(Hierarchy)와 상속

DDS 표준은 시스템의 복잡성을 관리하기 위해 엔티티 간의 포함 관계(Containment Relationship)를 정의하며, 이에 따라 리스너 인터페이스 역시 계층적인 구조를 가진다. 이 구조는 객체 지향 프로그래밍의 클래스 상속과는 다른 개념인 책임의 위임(Delegation of Responsibility) 구조를 형성한다.12

5.0.1 엔티티 포함 관계 및 리스너 매핑

  • DomainParticipant (최상위 엔티티) -> DomainParticipantListener
  • Publisher -> PublisherListener
  • DataWriter -> DataWriterListener
  • Subscriber -> SubscriberListener
  • DataReader -> DataReaderListener
  • Topic -> TopicListener

가장 하위 레벨인 DataWriterListenerDataReaderListener는 각각 PublisherListenerSubscriberListener의 하위 집합적인 성격을 갖지 않는다. 오히려 상위 리스너 인터페이스는 하위 리스너가 처리할 수 있는 모든 메서드를 포함하는 형태(Superset)로 정의된다. 예를 들어, DomainParticipantListener는 하위의 모든 엔티티(Publisher, Subscriber, Topic)에서 발생할 수 있는 모든 종류의 이벤트 메서드를 포함하고 있다.10

이러한 계층 구조의 목적은 중앙집중식 예외 처리를 가능하게 하는 데 있다. 수백 개의 DataReader가 존재하는 시스템에서 모든 DataReader마다 개별적으로 on_requested_deadline_missed를 구현하여 로깅하는 것은 비효율적이다. 대신, 최상위 DomainParticipantListener 하나에만 로깅 로직을 구현해두면, 하위 엔티티에서 처리되지 않은 모든 오류를 한곳에서 받아 처리할 수 있다.

6. 상태 전파(Status Propagation) 메커니즘과 규칙

리스너 모델의 가장 정교하면서도 개발자들이 자주 혼동하는 부분이 바로 상태 전파(Status Propagation) 규칙이다. 이벤트가 발생했을 때, DDS 미들웨어는 누가 이 이벤트를 처리할지를 결정하기 위해 엔티티 계층을 타고 올라가는 탐색 과정을 수행한다. 이를 ’가장 구체적인 리스너 우선 원칙(Most Specific Listener Rule)’이라 한다.10

6.0.1 전파 알고리즘

  1. 발생 지점 확인: 이벤트(예: REQUESTED_DEADLINE_MISSED)가 발생한 엔티티(예: DataReader)를 확인한다.
  2. 리스너 존재 및 활성 여부 확인:
  • 해당 엔티티에 리스너가 장착되어 있는가? (Not NIL)
  • 장착된 리스너의 StatusMask에 해당 이벤트 비트가 켜져 있는가? (Enabled)
  1. 실행 또는 전파:
  • Yes: 조건이 만족되면 해당 리스너의 콜백(on_requested_deadline_missed)을 호출하고 처리를 종료한다. 이벤트는 상위로 전파되지 않는다.
  • No: 조건이 만족되지 않으면(리스너가 없거나, 마스크가 꺼져 있거나), 이벤트는 부모 엔티티(예: Subscriber)로 전달된다.
  1. 반복: 부모 엔티티에 대해서도 동일한 검사를 수행한다. Subscriber -> DomainParticipant 순으로 올라간다.
  2. 최종 처리: 최상위 DomainParticipantListener조차 해당 이벤트를 처리하지 않거나(리스너가 없거나 마스크가 꺼짐), 처리했더라도 DDS 스펙에 따라 기본 동작이 수행될 수 있다.

6.0.2 예외: 데이터 가용성 상태의 전파

대부분의 상태는 위 규칙을 따르지만, **데이터 도착(DATA_AVAILABLE)**과 관련된 상태는 특수한 전파 규칙을 가진다.

  • DataReader에서 DATA_AVAILABLE 상태가 발생하고 처리되지 않으면, 이는 상위 SubscriberDATA_ON_READERS 상태로 변환되어 전파된다.
  • SubscriberListeneron_data_on_readers()가 호출되면, 이는 하위의 모든 DataReader에 대한 데이터 처리를 수행한다는 의미이다. 따라서 이 경우 하위 DataReaderListeneron_data_available은 호출되지 않는다.13
  • 만약 Subscriber도 이를 처리하지 않으면 DomainParticipanton_data_on_readers로 전파된다.

이 메커니즘은 개별 데이터 도착을 일일이 처리하는 대신, Subscriber 레벨에서 여러 토픽의 데이터를 묶어서 처리(Batch Processing)하거나 우선순위를 조정할 수 있는 기회를 제공한다.

7. StatusMask와 이벤트 필터링 전략

리스너를 등록한다고 해서 모든 이벤트에 대해 콜백을 받아야 하는 것은 아니다. 불필요한 콜백 호출은 컨텍스트 스위칭 비용과 CPU 부하를 유발하므로, 개발자는 StatusMask를 사용하여 관심 있는 이벤트만을 선별해야 한다.4

StatusMaskset_listener 메서드의 두 번째 인자로 전달되는 비트마스크이다.

// C++ 예시: 오직 데이터 도착과 마감 기한 위반만 감지하겠다고 설정
dds::core::status::StatusMask mask =
dds::core::status::StatusMask::data_available() |
dds::core::status::StatusMask::requested_deadline_missed();

reader.set_listener(listener, mask);

7.1 Nil 리스너와 Empty Mask의 결정적 차이

이벤트 전파 규칙과 관련하여, 리스너 객체를 NULL로 설정하는 것과, 객체는 있지만 StatusMask0(NONE)으로 설정하는 것은 동작이 완전히 다르다.3

  • Nil Listener (Listener = NULL): “나는 리스너가 없다.” -> 이벤트는 즉시 상위 엔티티로 전파된다.
  • Active Listener with Empty Mask (StatusMask = NONE): “나는 리스너가 있지만, 지금은 아무 소리도 듣고 싶지 않다.” -> 미들웨어는 리스너가 존재한다고 판단하여 상위로 전파하지 않는다. 하지만 마스크에 의해 콜백 호출도 차단된다. 결과적으로 해당 이벤트는 무시(No-Op)되어 소멸한다.

이 차이는 시스템 설계 시 매우 중요하다. 하위 엔티티의 특정 이벤트를 상위 레벨의 통합 로거가 기록하지 못하게 차단하고 싶다면, 하위 엔티티에 빈 마스크(Empty Mask)를 가진 더미 리스너를 등록하여 ’이벤트 블랙홀’을 만들 수 있다.

8. 비동기 리스너의 실행 모델과 스레드 컨텍스트

리스너 사용 시 가장 주의해야 할 점은 콜백 함수가 누구의 스레드에서 실행되는가이다. WaitSet 방식에서는 애플리케이션이 생성한 스레드가 데이터를 가져가지만, 리스너 방식에서는 **미들웨어가 생성한 내부 수신 스레드(Receive Thread)**가 애플리케이션의 코드를 호출한다.9

8.1 미들웨어 스레드 컨텍스트(Middleware Thread Context)의 의미

  1. 즉시성(Immediacy): 데이터가 네트워크 스택을 통과하여 역직렬화(Deserialization)되자마자, 동일한 스레드 흐름 내에서 즉시 on_data_available이 호출된다. 이는 큐잉(Queuing)이나 스레드 깨움(Waking up)에 따른 지연이 없음을 의미한다.
  2. 동시성 제약(Concurrency Constraints): 애플리케이션 코드가 미들웨어의 소유인 스레드를 ‘빌려’ 쓰는 것이다. 만약 콜백 함수 내에서 긴 시간을 소모하는 작업(예: 복잡한 연산, DB 쓰기, 파일 I/O)을 수행하면, 미들웨어의 수신 스레드가 차단된다. 이는 해당 스레드가 담당하는 다른 DataReaderTopic의 데이터 수신까지 지연시키는 결과를 초래한다.9
  3. 교착 상태(Deadlock) 위험: 콜백 함수 내에서 다시 DDS 미들웨어의 API를 호출할 때(예: 다른 DataWriter로 쓰기 작업을 하거나, 엔티티를 삭제하는 등), 미들웨어 내부의 락(Lock)과 충돌하여 교착 상태에 빠질 위험이 크다. 이에 대한 상세한 방지 가이드는 6.3절에서 다룬다.10

따라서, 리스너 구현의 대원칙은 **“가볍고 빠르게(Lightweight & Fast)”**이다. 무거운 처리가 필요하다면, 리스너는 데이터를 별도의 큐에 복사하고 이벤트 시그널만 보낸 뒤 즉시 리턴해야 하며, 실제 처리는 별도의 애플리케이션 워커 스레드(Worker Thread)가 수행하도록 해야 한다.

9. 리스너 vs WaitSet: 아키텍처 결정 매트릭스

DDS 시스템 설계자는 언제 리스너를 사용하고 언제 WaitSet을 사용할지 결정해야 한다. 두 모델은 상호 배타적이지 않으며 혼용 가능하다. 아래 표는 다양한 차원에서 두 모델을 비교 분석한 것이다.9

비교 항목리스너 (Listener)웨이트셋 (WaitSet/Condition)
제어 흐름Push (Active Middleware): 미들웨어가 주도하여 호출.Pull (Active Application): 앱이 주도하여 확인.
지연 시간 (Latency)최소 (Lowest): 데이터 도착 즉시 실행. 컨텍스트 스위칭 비용 없음.중간 (Medium): 스레드 시그널링 및 스케줄링 오버헤드 존재.
처리량 (Throughput)낮음/중간: 고빈도 데이터(High Frequency)의 경우 잦은 함수 호출 오버헤드 발생.높음 (High): 여러 샘플을 한 번에 가져와 처리(Batching)하기 유리함.
복잡성높음: 비동기 로직, 스레드 안전성(Thread Safety), 교착 상태 고려 필요.낮음: 절차적이고 동기적인 코드로 이해하기 쉽고 디버깅 용이.
데이터 접근이벤트 발생 시점에만 접근 가능.애플리케이션이 원할 때 언제든지 접근 가능.
QoS 이벤트 처리즉각적인 예외 처리에 적합 (예: Deadline Miss 알림).주기적인 상태 모니터링이나 통합 처리에 적합.
스레드 모델미들웨어 스레드 점유.애플리케이션 스레드 사용. 미들웨어 영향 없음.

9.1 권장 사용 시나리오

  • 리스너 권장:
  • 데이터 도착 빈도가 낮거나 불규칙한(Aperiodic) 경우 (예: 경보 신호, 설정 변경 명령).
  • 최소한의 지연 시간(Low Latency)이 절대적으로 중요한 경우 (예: 미사일 요격 시스템의 트리거).
  • 예외 상황(QoS 위반, 연결 끊김)에 대한 즉각적인 로깅 및 대응이 필요한 경우.
  • WaitSet 권장:
  • 고주파수(High Frequency)로 데이터가 쏟아지는 경우 (예: 1kHz 센서 데이터). 이 경우 리스너는 함수 호출 오버헤드로 인해 시스템을 마비시킬 수 있다.
  • 데이터 처리 로직이 복잡하고 시간이 오래 걸리는 경우.
  • 여러 데이터 스트림의 상관관계(Correlation)를 분석해야 하는 경우.

10. 결론: 이벤트 중심 DDS 애플리케이션 설계

DDS 리스너 모델은 분산 시스템의 복잡한 상태 변화를 이벤트라는 단위로 추상화하고, 이를 애플리케이션 로직에 즉각적으로 반영할 수 있게 하는 강력한 도구이다. 이는 폴링 방식의 비효율성을 제거하고 시스템의 반응성을 극대화한다. 그러나 제어의 역전이라는 특성상, 미들웨어의 스레드 컨텍스트를 공유한다는 점과 비결정적인 실행 시점을 고려한 신중한 설계가 요구된다.

성공적인 DDS 애플리케이션 개발을 위해서는 데이터의 특성(빈도, 중요도)에 따라 리스너와 WaitSet을 적절히 혼합하여 사용하는 하이브리드 아키텍처가 권장된다. 예를 들어, 핵심 제어 데이터는 WaitSet으로 배치 처리하여 처리량을 높이고, 시스템 상태 모니터링이나 긴급 명령은 리스너로 처리하여 반응성을 확보하는 식이다. 6.2절에서는 이러한 리스너 모델의 가장 핵심인 on_data_available 콜백 함수의 구체적인 구현 패턴과 데이터 접근 방식(take vs read)에 대해 상세히 다룰 것이다.

11. 참고 자료

  1. Waitsets | Data Distribution Service (DDS) Community RTI Connext Users, https://community.rti.com/examples/waitsets
  2. Exploiting Multi-Core with CoreDX Data Distribution Service - Twin Oaks Computing, Inc, https://www.twinoakscomputing.com/wp/CoreDX_DDS_Multicore.pdf
  3. Data Distribution Service (DDS) - Object Management Group (OMG), https://www.omg.org/spec/DDS/1.4/PDF
  4. Data Distribution Service for Real-Time Systems Specification - Object Management Group (OMG), https://www.omg.org/spec/DDS/1.1/PDF/
  5. Interrupts vs. Polling: What’s the Dif-(Interrupt)-ference? – Digilent Blog, https://digilent.com/blog/interrupts-vs-polling-whats-the-dif-interrupt-ference/
  6. Polling vs Interrupts: Exploring their Differences and Applications - Total Phase, https://www.totalphase.com/blog/2023/10/polling-interrupts-exploring-differences-applications/
  7. Javanotes 9, Section 1.2 – Asynchronous Events: Polling Loops and Interrupts, https://math.hws.edu/javanotes/c1/s2.html
  8. OMG Data-Distribution Service: Architectural Overview, https://d2vkrkwbbxbylk.cloudfront.net/sites/default/files/DDS_Architectural_Overview.pdf
  9. Use WaitSets, Except When You Need Extreme Latency | Data Distribution Service (DDS) Community RTI Connext Users, https://community.rti.com/best-practices/use-waitsets-except-when-you-need-extreme-latency
  10. Package: DDS.Listener - RTI Community, https://community.rti.com/static/documentation/connext-dds/current/doc/api/connext_dds/api_ada/dds-listener.ads.html
  11. Getting Started - OpenDDS 3.33.0, https://opendds.readthedocs.io/en/latest-release/devguide/getting_started.html
  12. Conditions and Listeners - OpenDDS 3.34.0-dev - Read the Docs, https://opendds.readthedocs.io/en/latest/devguide/conditions_and_listeners.html
  13. 3.1.3. Status - 3.4.1 - eProsima Fast DDS, https://fast-dds.docs.eprosima.com/en/3.x/fastdds/dds_layer/core/status/status.html
  14. OpenSplice Java 5 DCPS: org.omg.dds.sub.Subscriber Interface Reference, https://download.zettascale.online/www/docs/Vortex/apis/ospl/java5/html/a02397.html
  15. Specification of DDS Service Discovery Protocol - AUTOSAR.org, https://www.autosar.org/fileadmin/standards/R25-11/FO/AUTOSAR_FO_PRS_DDSServiceDiscoveryProtocol.pdf
  16. RTI Connext C API: Status Kinds, https://community.rti.com/static/documentation/connext-dds/current/doc/api/connext_dds/api_c/group__DDSStatusTypesModule.html
  17. Listeners - RTI Community, https://community.rti.com/static/documentation/connext-dds/current/doc/manuals/connext_dds_professional/users_manual/users_manual/Listeners.htm
  18. RTI Connext Modern C++ API: Listener Class Reference - RTI Community, https://community.rti.com/static/documentation/connext-dds/current/doc/api/connext_dds/api_cpp2/classListener.html
  19. Qt and RTI DDS interaction—Need some guidance - Stack Overflow, https://stackoverflow.com/questions/17834696/qt-and-rti-dds-interaction-need-some-guidance
  20. Evaluating the Performance of Publish/Subscribe Platforms for Information Management in Distributed Real-time and Embedded Systems - DDS Foundation, https://www.dds-foundation.org/sites/default/files/Evaluating_Performance_Publish_Subscribe_Platforms.pdf