Day5 线程模型与异步处理

4 阅读5分钟

1. 核心线程架构全景

1.1 全局线程架构全景图

Fast-DDS 3.6.x采用分层并发模型,将计算密集型与IO密集型操作解耦,支持通过ThreadSettings QoS配置所有线程属性:

graph TB
    subgraph DomainParticipantThreadPool["DomainParticipant Thread Pool"]
        E["Event Thread: dds.ev.<participant_id>"] -->|"定时调度"| TE["TimedEvent队列: Heartbeat/Nack/KeepAlive"]
        AW["Async Writer Thread: dds.asyn.<pid>.<idx>"] -->|"流量控制"| FC["FlowController: FIFO/RR/Priority"]
        DS["Discovery Server Event: dds.ds_ev.<participant_id>"] -->|"数据库同步"| DB["Discovery DB: PDP/EDP同步"]
        SL["Security Logging: dds.slog.<participant_id>"] -->|"审计日志"| SEC["SecurityManager"]
    end
    
    subgraph TransportLayer["Transport Layer Threads"]
        RUDP["Reception UDP: dds.udp.<port>"] -->|"recvmsg"| MSG["RTPS Message解析"]
        RTCP["Reception TCP: dds.tcp.<port>"] -->|"stream read"| MSG
        RSHM["Reception SHM: dds.shm.<port>"] -->|"segment监听"| MSG
        TKA["TCP Keep Alive: dds.tcp_keep"] -->|"心跳检测"| CONN["TCP连接状态"]
        WSHM["SHM Watchdog: dds.shm.wdog"] -->|"健康监控"| SEG["共享内存段清理"]
    end
    
    subgraph BackgroundServices["Background Services"]
        LOG["Logging Thread: dds.log"] -->|"异步消费"| LOGQ["Log队列"]
        DSH["Datasharing Listener: dds.dsha.<id>"] -->|"零拷贝通知"| DR["DataReader"]
    end
    
    MSG -->|"触发回调"| E
    FC -->|"批量发送"| RUDP
    TE -->|"触发"| AW

技术细节:2. Library Overview - 3.6.0

• 线程命名规范:3.6.x引入标准化线程命名(dds.{type}.{id}),便于调试和性能分析

• Event Thread:基于asio::io_service的事件循环,处理周期性事件(默认1ms精度),通过ResourceEvent类实现

1.2 异步写处理流程(ASYNCHRONOUS_PUBLISH_MODE)

当配置异步发布模式时,写操作解耦为入队-调度-发送三阶段:

sequenceDiagram
    participant User as User Thread
    participant DW as DataWriter
    participant FC as FlowController<br/>/Priority/RR/FIFO
    participant AW as AsyncWriterThread<br/>dds.asyn.*
    participant Trans as Transport Layer
    
    User->>DW: write(data)
    DW->>DW: 序列化+加密(Security)
    
    alt ASYNCHRONOUS模式
        DW->>FC: add_new_sample(change)<br/>入队待发送
        FC->>FC: 令牌桶计算<br/>max_bytes_per_period/period_ms
        FC-->>User: 立即返回OK<br/>(非阻塞)
        
        loop AsyncWriterThread::run()
            AW->>FC: get_next_change()<br/>调度策略选择
            FC-->>AW: CacheChange_t*
            AW->>Trans: send_data()<br/>实际网络IO
            Trans-->>AW: 发送结果
            AW->>DW: 触发Listener回调<br/>on_publication_matched
        end
    else SYNCHRONOUS模式
        DW->>Trans: 直接send_data()<br/>用户线程上下文
        Trans-->>User: 阻塞返回
    end

关键技术实现(src/cpp/rtps/resources/AsyncWriterThread.cpp):

机制实现细节源码参考
唤醒机制condition_variable + mutex,3.6.x引入AsyncInterestTree优化感兴趣写者集合GitHub Discussion #2456
流量整形基于FlowController的令牌桶算法,支持4种调度策略:FIFO、ROUND_ROBIN、HIGH_PRIORITY、PRIORITY_WITH_RESERVATION15.5. Large Data Rates - 3.6.0
批量发送RTPSMessageGroup聚合多个小样本,减少系统调用;受max_bytes_per_batch限制/home/guang/code/opensource/Fast-DDS/src/cpp/rtps/messages/RTPSMessageGroup.cpp

1.3 ResourceEvent 定时器架构

Event Thread是Fast-DDS的中枢神经系统,管理所有时间触发逻辑:

flowchart TD
    A[ResourceEvent::run<br/>dds.ev.*] --> B{asio::io_service::run_one}
    B -->|超时/事件| C[process_event]
    
    C --> D{事件类型}
    D -->|定时到期| E[TimedEvent::trigger<br/>虚拟函数]
    D -->|异步IO就绪| F[传输层回调<br/>TCP/SHM]
    D -->|用户定时| G[Listener::on_trigger]
    
    E --> H[重新计算expiry_time]
    H --> I[asio::steady_timer::expires_at<br/>堆结构管理]
    I --> B
    
    subgraph "TimedEvent派生类实例"
        PH[PeriodicHeartbeat<br/>StatefulWriter]
        NR[NackResponseDelay<br/>重传抑制]
        PL[WriterLiveliness<br/>保活检测]
        RP[ResendParticipantData<br/>发现重传]
    end
    
    E --> PH
    E --> NR
    E --> PL
    E --> RP
    
    style A fill:#f9f,stroke:#333,stroke-width:2px
    style I fill:#bbf,stroke:#333,stroke-width:2px

技术细节

  • 定时精度:使用asio::steady_timer(单调时钟),避免系统时间回拨影响
  • 线程安全ResourceEvent类在3.6.x中已移至src/cpp目录(非公开API),通过TimedEventImpl管理生命周期
  • 优先级:支持通过ThreadSettings配置线程优先级、CPU亲和性、堆栈大小

2. 适用场景与注意事项

2.1 场景适配指南

场景推荐配置线程关注重点风险点
高吞吐发布ASYNCHRONOUS_PUBLISH_MODE + 自定义FlowController(FIFO/RR)监控dds.asyn.* CPU占用,避免流控令牌不足导致队列膨胀异步队列堆积导致内存溢出;需配置max_bytes_per_period
低延迟通信SYNCHRONOUS_PUBLISH_MODE + 禁用Nagle算法关注dds.ev.*调度延迟,避免心跳定时器抖动用户线程阻塞影响业务;大消息序列化耗时
大规模节点增大Event Thread优先级,启用Discovery Serverdds.ds_ev.*成为瓶颈,需监控数据库锁竞争发现风暴导致线程饥饿;需配置max_bytes_per_period限制Builtin流量
共享内存传输SHM Transport + 独立Watchdog注意dds.shm.wdog对segment异常的清理僵尸共享内存段残留;Windows平台SHM端口竞争
RPC服务自定义RpcServerSchedulingStrategy(线程池模式)thread_pool_size配置影响并发处理能力直接调度策略(Direct)可能导致栈溢出;feed参数处理需谨慎

2.2 关键注意事项

线程死锁风险:

3.6.x之前版本AsyncWriterThread在DomainParticipant析构时可能出现condition_variable销毁顺序问题(先销毁锁后销毁条件变量导致hang),3.6.x通过Semaphore和ResourceEvent重构修复Previous end-of-life versions - 3.6.0

• 禁止在Listener回调中调用阻塞API:Event Thread执行Listener回调时,阻塞操作会延迟所有定时事件处理

CPU亲和性与实时性:

• 通过ThreadSettingsQoS可配置:

thread_settings.set_scheduler_priority(60);  // Linux实时优先级
thread_settings.set_affinity({0, 2});        // 绑定CPU核心
thread_settings.set_stack_size(4096*1024);   // 8MB栈空间

• MacOS限制:3.3.1修复了线程亲和性设置为0的问题 Previous end-of-life versions - 3.6.0

内存与资源管理:

• Datasharing Listener:每DataReader独立线程(dds.dsha.*),零拷贝场景下线程数随Reader线性增长,需监控线程资源消耗

• Security Logging:独立线程(dds.slog.*),启用Security时需额外考虑日志队列内存

诊断命令:

# 查看Fast-DDS线程实时状态
gdb -p $(pgrep your_app) -ex "info threads" -ex "thread apply all bt" --batch

# 3.6.x特有:检查FlowController队列堆积
# 在FlowControllerImpl::get_next_change()打断点,观察unsent_changes_.size()

3. 参考数据源

3.1 官方文档与规范

数据源类型链接/标识说明
官方文档Fast DDS 3.6.0 Library Overview - Threading Model权威线程类型与命名规范定义
FlowController配置指南Large Data Rates - Flow Controllers流量控制策略与配置示例
RPC线程调度Customizing RPC Server request scheduling服务端线程池与调度策略
迁移指南Migration Guide to Fast DDS v33.x线程类API变更(ResourceEvent等移至internal)

3.2 源码实现参考(基于用户本地路径)

组件源码路径关键类/函数
异步写线程/home/guang/code/opensource/Fast-DDS/src/cpp/rtps/resources/AsyncWriterThread.cppAsyncWriterThread::run(), add_flow_controller()
流量控制器/home/guang/code/opensource/Fast-DDS/src/cpp/rtps/flowcontrol/FlowControllerImpl, SchedulerPolicy枚举
事件系统/home/guang/code/opensource/Fast-DDS/src/cpp/rtps/resources/ResourceEvent.cppResourceEvent::run(), TimedEvent基类
线程设置/home/guang/code/opensource/Fast-DDS/src/cpp/fastdds/core/thread_settings/ThreadSettingsQoS, apply_thread_settings()
传输层接收/home/guang/code/opensource/Fast-DDS/src/cpp/rtps/transport/UDPChannelResource, SharedMemChannelResource
日志线程/home/guang/code/opensource/Fast-DDS/src/cpp/fastdds/log/Log::run()(dds.log线程)

3.3 社区与学术讨论

类型链接内容摘要
架构讨论GitHub Discussion #2456: Multithreading architecture社区关于Reception线程数量、AsyncWriterThread生命周期的技术讨论
ROS2集成Configuring Fast DDS in ROS 2RMW层线程配置与RMW_FASTRTPS_PUBLICATION_MODE环境变量
版本发布说明Previous versions - 2.13.0ThreadSettingsQoS首次引入版本(2.13.0)及后续修复

3.4 专利与技术渊源 • DDS标准: OMG DDS Specification v1.4 (线程模型符合DDS规范的并发要求)

• RTPS协议: OMG RTPS 2.5 (Real-Time Publish-Subscribe) - 事件驱动架构基于RTPS状态机实现

• Asio库: Fast-DDS底层使用Boost.Asio (Eprosima内部实现eprosima::fastdds::rtps::AsioServiceTie封装)

3.5 总结

该线程模型通过职责分离(IO与计算分离)、资源隔离(每DomainParticipant独立Event Loop)、背压机制(FlowController限流),支撑了从嵌入式设备到云端服务的全场景DDS通信需求。3.6.x版本的关键改进在于全线程可配置性(ThreadSettingsQoS覆盖所有线程类型)和FlowController粒度细化(支持Builtin Writers流量控制)。

4 调试技巧