RTI_DDS线程模型

本章介绍Connext DDS用于发送和接收数据的内部线程,维护内部状态,并在事件发生时调用用户代码,例如新DDS的到来
数据样本。 了解这些线程如何与您的交互可能很重要应用。

DomainParticipant使用三种类型的线程。 实际的线程数取决于配置各种QosPolicies以及执行所使用的传输DomainParticipant发送和接收数据。

通过各种QosPolicies,用户应用程序可以配置优先级和其他属性。Connext DDS创建的线程。 在实时系统中,用户通常需要设置应用程序中所有线程相对于彼此的优先级,以便系统正常运行。

本章包括

20.1数据库线程

    Connext DDS使用内部数据结构来存储关于本地创建和重新发现的实体的信息。此外,它还会存储Connext DDS使用的各种对象和数据保持应用程序之间的正确通这个“数据库”是为每个DomainParticipant创建的。
    由于实体和对象在用户应用程序的正常操作期间被创建和删除,数据库中的不同条目也可能被创建和删除。因为多个线程可能

同时访问存储在数据库中的对象,从中删除和删除一个对象数据库分两个阶段发生,以支持线程安全。

    当通过用户代码的操作或结果删除数据库中的条目或者对象时系统状态发生变化时,仅标记为删除。它不能被实际删除和从数据库中删除,直到Connext DDS可以确定没有线程仍在访问该对象。相反,对象的实际移除被委托给Connext DDS的内部线程产生定期唤醒并清除已删除对象的数据库。这个线程被称为数据库线程(也称为数据库清理线程)。

    每个DomainParticipant只创建一个数据库线程

DomainParticipant的DATABASE QosPolicy(DDS扩展)配置数据库使用的资源以及清理线程的属性。具体来说,用户可能想要使用此QosPolicy来设置优先级,堆栈大小和线程选项清理线程。您必须在创建DomainParticipant之前设置这些选项,因为一旦清理线程作为参与者创建的一部分启动,这些属性不能更改。
    数据库清理线程唤醒以清除已删除对象的时间段也设置在数据库QosPolicy。通常情况下,这段时间从那里开始设置很长一段时间(大约一分钟)没有必要浪费CPU周期来唤醒线程而仅仅发现无所事事。

    但是,当DomainParticipant被销毁时,会由DomainParticipant创建的所有对象也将被销毁。这些对象中的很多都存储在数据库中,因此必须被清理线程销毁。DomainParticipant不能被销毁,直到数据库为空并且自身被销毁。因此,DATABASE QosPolicy中有一个不同的参数,shutdown_cleanup_period,在DomainParticipant被销毁时由数据库清理线程使用。通常设置为秒级,这个参数减少了额外的时间来销毁一个DomainParticipant仅仅是因为等待清理线程唤醒并清除数据库。

20.2事件线程

    在运行过程中,Connext DDS必须以不同的时间间隔唤醒,以检查许多不同时间触发事件或周期性事件的情况。这些事件通常用于确定是否发生了某些事情或者没有在指定的时间内发生。通常情况下,必须定期检查条件,只要条件适用的实体仍然存在。另外,DomainParticipant可能需要做些事情定期维护与远程实体的连接。
    例如,DEADLINE QosPolicy用于确保DataWriters已发布数据或DataReaders已在指定时间段内收到数据。同样,LIVELINESS QosPolicy配置Connext DDS,以定期检查DataWriter是否发送了活动性消息并定期发送活动性消息代表DataWriter。作为最后一个例子,对于可靠的连接,心跳必须定期从DataWriter发送到DataReader,以便DataReader可以确认它的数据已收到。

Connext DDS使用内部线程(称为事件线程)执行以下操作:

  • 检查是否错过了最后期限
  • 调用用户安装的侦听器回调来通知应用程序错过最后期限
  • 发送心跳信号以保持可靠的连接

注意:每个DomainParticipant只创建一个事件线程。

    DomainParticipant的EVENT QosPolicy(DDS扩展)配置了事件线程的属性和资源。具体来说,用户可能想要使用它
QosPolicy设置Event线程的优先级,堆栈大小和线程选项。您必须设置这些选项在创建DomainParticipant之前,因为一旦事件线程作为参与者的一部分启动创造,这些属性不能改变。
    EVENT QosPolicy还配置事件可以处理的最大事件数量。虽然Event线程一次只能处理一个事件,但它必须保持一个队列来保存正在等待的事件。 QosPolicy的initial_count和max_count参数设置了初始和队列的最大大小。
    事件线程的优先级应该根据其他线程的优先级仔细设置一个系统。虽然许多事件可以容忍事件到期之间的某些延迟时间以及事件线程为事件提供服务的时间,可能会有特定于应用程序的事件尽快处理。
    例如,如果应用程序使用远程DataWriter的活跃性来推断的一个远程应用程序的正确操作,它可能对DataReader Listener回调,on_liveliness_chaged(),一旦可以确定远程应用程序已经被事件线程调用死了。操作系统使用事件线程的优先级来调度此操作。

20.3接收线程

    Connext DDS使用内部线程(称为接收线程)来处理通过底层网络传输接收的数据包。这些数据包可能包含由DomainParticipants交换的用于发现的元流量,或用于支持可靠连接的用户数据(以及支持可靠连接的元数据)的DataReader。
    由于处理传输接收到的数据包,接收线程可能会通过在网络上发送数据包进行响应。发现数据包可能会发送给其他DomainParticipants以响应其中一个接收。响应于心跳而发送ACK / NACK包以支持可靠的连接。

    当DDS样本到达时,接收线程负责将数据反序列化并存储数据在接收DataReader的队列以及调用on_data_available()DataReaderListener回调。

    Connext DDS将为DomainParticipant创建的接收线程数取决于方式您已经配置了DomainParticipants,DataWriters和DataReaders的QosPolicies以及特定的运输实现。内置传输的行为已经详细说明:

    但是,如果为DomainParticipant安装了自定义传输,则必须了解自定义传输可以预测将创建多少个接收线程。以下讨论适用于每个传输的基础上。一个接收线程只会服务一个单一传输。
    Connext DDS将尝试为其配置为接收消息的每个传输的每个端口创建接收资源。 TRANSPORT_UNICAST QosPolicy(DDS扩展)对于DomainParticipant,DataWriters和DataReaders,TRANSPORT_MULTICAST,用于DataReaders的QosPolicy(DDS扩展)和用于DomainParticipants的DISCOVERY的QosPolicy(DDS扩展)都配置了Connext DDS尝试用于接收消息的端口数量和传输数量。
    通常,传输将需要Connext DDS为每个唯一端口创建一个新的接收资源数。但是,这取决于底层物理传输的工作方式以及Connext DDS使用的传输插件的实现。有时Connext DDS只需要创建一个单个接收任何端口号的端口资源。
    当Connext DDS发现它被配置为接收一个传输的某个端口上的数据时,但是它还没有创建了一个接收资源,它会询问传输是否有任何现有的接收资源被创建为运输可以共享。如果是这样,那么Connext DDS将不必创建新的接收资源。如果不,然后Connext DDS会创建新的资源。
    TRANSPORT_UNICAST,TRANSPORT_MULTICAST和DISCOVERY QosPolicies允许您可以自定义接收用户数据的端口(基于每个DataReader)和元流量(DataWriters和DomainParticipants);对于单播和多播,端口的设置也可以不同。
    接收资源如何与接收线程相关? Connext DDS将创建一个接收线程来服务每一个创建的接收资源。如果您使用套接字比喻,那么对于每个创建的套接字,Connext DDS将使用单独的线程来处理在该套接字上收到的数据。
    那么默认情况下Connext DDS将创建多少个线程 - 仅使用内置的UDPv4并共享内存传输和不修改任何QosPolicies?
为元流量(元流量是指与动态发现相关的Connext DDS内部的流量)创建三个接收线程:

  • 用于单播(一个用于UDPv4,一个用于共享内存)
  • 用于多播(用于UDPv4),(共享内存传输不支持多播)

为用户数据创建的两个接收线程:

  • 2为单播(UDPv4,共享内存)
  • 0表示多播(因为默认情况下用户数据不是通过多播发送的)

    因此,默认情况下,每个DomainParticipant总共有五个接收线程。 只使用一个传输和禁用多播,一个DomainParticipant可以有少至2个接收线程。类似于数据库和事件线程,接收线程由RECEIVER_POOL进行配置QosPolicy(DDS扩展)。 但是,请注意,中的线程属性RECEIVER_POOL QosPolicy适用于为DomainParticipant创建的所有接收线程。

20.4专用区域,Connext DDS线程和用户监听器

    Connext DDS事件和接收线程可以通过安装的Listener回调调用用户代码在执行内部Connext DDS代码时在不同的实体上。反过来,在回调里面的用户代码可能会调用重新进入Connext DDS内部代码空间的Connext DDS API。为了线程安全,

Connext DDS分配并使用互斥信号(互斥)。

    正如专用区域(EA)中讨论的那样,当多个线程和多个线程时互斥体混在一起,可能导致死锁。为了防止发生死锁,Connext DDS是设计时使用了仔细的分析和遵循的规则,强制mutexes在某些时候被采取一个线程必须同时采用多个互斥锁。

    但是,因为事件和接收线程在调用用户回调时已经持有互斥体,并且因为用户代码可以调用的Connext DDS API可能会尝试使用其他互斥锁和死锁可能仍然会导致。因此,为了防止用户代码导致内部Connext DDS线程死锁,我们有创建了一个名为Exclusive Areas(EA)的概念,遵循防止死锁的规则。那更多的EA存在于系统中,Connext DDS代码允许的并发性越高。但是,更多的EA存在的情况下,允许在Entity Listener中调用的Connext DDS API的限制更多回调。
    EXCLUSIVE_AREA QosPolicy(DDS扩展)控制了多少个EA将由Connext DDS创建。有关EA的更详细的讨论以及对EA的限制在Entity Listener方法中使用Connext DDS API,请参阅专用区域(EA)。

20.5 控制RTI线程的CPU内核亲和性

DDS_ThreadSettings_t结构中的两个字段是与CPU核心关联性有关:cpu_list和cpu_rotation。

注意:虽然DDS_ThreadSettings_t用于Event,Database,ReceiverPool和AsynchronousPublisher QoS策略,但cpu_list和cpu_rotation仅与RECEIVER_POOL相关QosPolicy(DDS扩展)。
    尽管大多数与线程相关的QoS设置适用于单个线程,但ReceiverPool QoS策略的线程设置控制每个创建的接收线程。在这种情况下,有几种方案将M个线程映射到N个处理器; cpu_rotation控制使用哪个方案。
    cpu_rotation确定cpu_list如何影响与线程相关的QoS策略的处理器关联适用于多个线程。如果cpu_list为空,则cpu_rotation无关紧要,因为不会进行关联调整发生。假设cpu_list = {0,1},中间件创建三个接收线程{A,B,C}。如果cpu_rotation设置为CPU_NO_ROTATION,则线程A,B和C将具有相同的处理器亲和力(0-1),操作系统将控制这个界限内的线程调度。
    CPU亲和力通常用位掩码表示,其中置位表示允许的处理器运行上。该掩码以十六进制打印,因此可以用掩码0x3表示0-1的CPU关联。
    如果cpu_rotation是CPU_RR_ROTATION,则每个线程将以循环方式分配给其中的一个cpu_list中的处理器;也许线程A为0,B为1,C为0.请注意内部的顺序中间件线程派生是未指定的。

RTI Connext DDS核心库平台注释描述哪些体系结构支持此功能

20.6用XML配置线程设置

表20.1 ThreadSettings_t的XML标记描述了可用于配置线程的XML标记

设置。 略。

20.7个用户管理线程

在某些情况下,您可能需要完全控制由Connext DDS创建的内部线程应用。例如,在内存受限的系统中,应用程序可能需要管理资源内部Connext DDS线程需要。此外,你可能想要使用不同的线程技术一个Connext DDS默认合并(即POSIX平台上的pthread)。
    Connext DDS可以通过抽象工厂模式从应用程序层创建内部线程。您可以为Connext DDS应用程序提供一个ThreadFactory实现,DomainParticipants将使用该实现来创建和删除所有线程。

    ThreadFactory接口公开用于创建和删除线程的操作。这些操作是因为DomainParticipants需要新的线程或需要删除现有的线程。

    同一个ThreadFactory实例可以被多个DomainParticipants使用。选择哪个要使用ThreadFactory,请使用DomainParticipantFactory中的set_thread_factory()操作:

MyThreadFactory myThreadFactory; //实现DDSThreadFactory
    retcode = DDSTheParticipantFactory-> set_thread_factory(&myThreadFactory);

    然后,您可以使用任何可用的API(即create_participant(),create_participant_from_config()等)创建DomainParticipants。 DomainParticipant将使用设置的ThreadFactory对象在DomainParticipantFactory创建时以及整个生命周期中。 如果一个新的ThreadFactory已设置,现有的DomainParticipants不会受到影响; 他们仍然会使用相同的
用它们创建的ThreadFactory。该功能仅适用于C / C ++ API。 有关更多信息,请参阅API参考HTML文档

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

道格拉斯范朋克

播种花生牛奶自留田

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值