目录
本文主要解读V1.4版本DDS标准Subscription模块中数据的读取
数据访问
在DataReader对象上通过以下操作使用数据:read, read_w_condition, take, and take_w_condition。“read”操作的一般语义是应用程序只能访问相应的数据;中间件仍然负责管理和保存数据,并且可以再次读取。 “take”操作的语义是应用程序对数据负责;那些数据将不能再能通过DataReader访问。因此,DataReader可以多次通过read访问同一个样本。
这些操作每一个都返回一个有序的数据值和关联的SampleInfo对象的集合。每个数据值代表一个数据信息的原子(即,一个instance的值)。这个集合可能包含与相同或不同instances (由key识别)相关的样本。如果设置了相关HISTORY Qos(HISTORY QoS,2.2.3.18)允许的话,多个样本可以引用同一个instance。
SampleInfo
SampleInfo
包含了与Data值相关的信息:
sample_state
对于每个接收到的样本,中间件内部会针对每个 DataReader 维护一个 sample_state。值有两个:READ
或者 NOT_READ
。在由 read 或 take 方法返回的样本集合中,每个样本的 sample_state 通常是不同的。
READ
:表示 DataReader 已经通过 read 方法访问过该样本NOT_READ
表示 DataReader 之前还没有访问过该样本。
instance_state
对于每个实例,中间件内部维护一个 instance_state
。instance_state
可以是 ALIVE
、NOT_ALIVE_DISPOSED
或 NOT_ALIVE_NO_WRITERS
。
- ALIVE:表示该实例满足以下条件:
- a. 已经接收到该实例的样本;
- b. 有活跃的 DataWriter 实体正在写入该实例;
- c. 该实例没有被显式地处理,或者处理后又收到了更多的样本
NOT_ALIVE_DISPOSED
:表示该实例被 DataWriter 显式地处理了,即通过dispose
操作进行了处理NOT_ALIVE_NO_WRITERS
:表示DataReader 检测到没有活跃的 DataWriter 实体在写入该实例,因此将其声明为非活跃状态。
导致 instance_state
变化的具体行为事件取决于 OWNERSHIP QoS 的设置:
- 如果
OWNERSHIP
设置为EXCLUSIVE
,则只有“拥有”该实例的 DataWriter 显式地处置它时,instance_state
才会变为NOT_ALIVE_DISPOSED
。而instance_state
只有在拥有该实例的 DataWriter 写入该实例时才会再次变为ALIVE
。 - 如果
OWNERSHIP
设置为SHARED
,则只要任何一个 DataWriter 显式地处置该实例,instance_state
就会变为NOT_ALIVE_DISPOSED
。而instance_state
会在任何一个 DataWriter 再次写入该实例时变为ALIVE
。
SampleInfo
中的 instance_state
是在收集数据时(即调用读取 read
或 take
方法时)实例状态的一个快照。因此,在返回的集合中,所有指向同一个实例的样本其 instance_state
是相同的。
valid_data
通常每个DataSample包含一个SampleInfo
和数据Data。但是有些情况下,DataSample只包含SampleInfo
,而没有关联的数据,这种情况发生在服务通知应用程序某个instance的状态变化是由某些内部机制(比如超时)引起的,而这种状态变化并没有关联的数据。比如,当服务检测到某个instance没有任何writers时,会将相应的instance_state更改为NOT_ALIVE_NO_WRITERS
。
中间件在哪些具体场景下返回不包含数据的DataSample是依赖于实现的。应用程序可以通过检查valid_data
来区分一个特定的DataSample是否包含数据。如果为TRUE则包含有效的数据,反之则没有数据。
为了确保正确性和可移植性,在访问DataSample关联的数据之前,应用程序必须检查valid_data标志位;如果该标志位被设置为False,应用程序不应该访问与DataSample关联的数据,也就是说,只应该访问SampleInfo。
disposed_generation_count / no_writers_generation_count
对于每个instance,中间件内部维护了两个计数器:disposed_generation_count
和no_writers_generation_count
。这两个计数器是相对于每个DataReader的:
- 当DataReader第一次检测到一个从未见过的新的instance时,
disposed_generation_count
和no_writers_generation_count
初始化为零。 - 当instance对应的instance_state从
NOT_ALIVE_DISPOSED
变为ALIVE
时,disposed_generation_count
计数器增加。 - 当instance的
instance_state
从NOT_ALIVE_NO_WRITERS
变为ALIVE
时no_writers_generation_count
计数器增加
SampleInfo
中可用的 disposed_generation_count
和 no_writers_generation_count
在样本接收时捕获了对应计数器的快照。
SampleInfo sample_rank, generation_rank, and absolute_generation_rank
在 SampleInfo 中提供的 sample_rank
和 generation_rank
是基于由 read
或 take
返回的有序集合中的实际样本计算得出的:
sample_rank
表示在同一集合中与当前样本相同实例的后续样本的数量。- SampleInfo 中的
generation_rank
表示样本(S)与同一实例在返回集合中出现的最新样本(MRSIC)之间的“代数”差异。也就是说,它记录了从接收到样本 S 到接收到最新样本 MRSIC 这段时间内,该实例从非活跃状态转变为活跃状态的次数。generation_rank
的计算公式:
generation_rank =
(MRSIC.disposed_generation_count + MRSIC.no_writers_generation_count)
- (S.disposed_generation_count + S.no_writers_generation_count)
在 SampleInfo 中提供的 absolute_generation_rank
表示样本(S)与中间件已接收的同一实例的最新样本(MRS)之间的“代数”差异。也就是说,它记录了从接收到样本 S 到调用 read
或 take
这段时间内,该实例从非活跃状态转变为活跃状态的次数。计算方式如下:
absolute_generation_rank =
(MRS.disposed_generation_count + MRS.no_writers_generation_count)
- (S.disposed_generation_count + S.no_writers_generation_count)
view_state
每个instance会维护一个view_state
,值可以是NEW
或者 NOT_NEW
NEW
:表示这是DataReader 第一次访问这个instance的samples,或者DataReader之前已访问过该实例的samples,但是该实例随后又经历了重生(从not-alive到alive)。这两种情况可以通过检查disposed_generation_count
和no_writers_generation_count
区分;NOT_NEW
:表示DataReader已经访问过同一instance的sample,并且之后这个instance一直没有重生
SampleInfo 中提供的 view_state
是在获取集合时(即调用 read
或 take
时)相对于用于访问样本的 DataReader 的实例 view_state
的快照。因此,在返回的集合中指向同一实例的所有样本的 view_state 是相同的。
一旦检测到某个instance没有任何live的Writers,并且与该实例相关的所有样本都已经被从 DataReader 中取走后,中间件就可以回收所有关于该实例的本地资源。未来的样本将会被当作从未见过来处理。
状态图:TODP P63
数据访问方式
应用程序通过 DataReader 上的 read 或 take 操作来访问数据。这些操作返回一个有序的 DataSamples 的集合,其中每个样本包含 SampleInfo 部分和 Data 部分。中间件构建这个集合的方式取决于设置在 DataReader 和 Subscriber 上的QoS策略,以及sample的源时间戳,还有传递给 read/take 操作的参数,具体包括:
所需的sample states(即 READ
,NOT_READ
,或两者)。
所需的view states(即 NEW
,NOT_NEW
,或两者)。
所需的instance states(ALIVE
,NOT_ALIVE_DISPOSED
,NOT_ALIVE_NO_WRITERS
,或这些状态的组合)。
read
和 take
操作是非阻塞的,它们只会交付当前可用并与指定状态匹配的数据。
read_w_condition
和 take_w_condition
操作接受一个ReadCondition
对象作为参数,而不是上述的read的参数如样本状态、视图状态和实例状态。其行为是返回的样本仅限于那些满足该条件为 TRUE 的样本。这些操作结合 ReadCondition 对象和 WaitSet,允许执行等待读取。
一旦数据样本对数据读取者可用,应用程序可以选择读取或获取这些数据。基本规则是应用程序可以按任意顺序进行此操作。这种方法非常灵活,给予应用程序最大的控制权。然而,如果应用程序需要按照接收到的正确顺序检索样本,或者希望访问一组完整的连贯变更,则必须使用特定的访问模式。
为了连贯地或按顺序访问数据,必须正确设置 PRESENTATION QoS(在 2.2.3.6 节中解释),并且应用程序必须遵循下面描述的访问模式。否则,应用程序仍然可以访问数据,但不一定能看到所有连贯的更改一起出现,也无法按正确的顺序看到更改。
有一种通用模式可以在多个 DataReaders 上提供有序和连贯的访问。这种模式适用于任何 PRESENTATION QoS 设置。对于特定的 QoS 设置,更简单的模式也可能适用,如下所述:
- 跨DataReader实体以连贯集或按顺序访问samples的通用模式:这种情况适用PRESENTATION QoS指定“
access_scope=GROUP
”
- 在通知SubscriberListener或启用StatusCondition 后,应用程序使用Subscriber 的
begin_access
表明可以通过Subscriber访问data; - 然后调用Subscriber 的
get_datareaders
方法获取数据样本可用的DataReader的对象列表; - 接下来,它按照返回的相同顺序在每个DataReader上调用
read
或take
方法来访问DataReader中的所有changes - 一旦在所有DataReader上调用了take或read方法,就调用
end_access
注意,如果PRESENTATION QoS指定ordered_access=TRUE
,DataReaders 的列表可能会多次返回相同的reader。通过这种方式可以在不同DataReader对象样本之间保持正确的样本顺序。
2. 如果跨DataReader实体不需要保持任何顺序或连贯性,则采用专门的模式。这种情况适用于 PRESENTATION QoS 策略指定 access_scope
为除 GROUP
之外的其他值时。
- 这种情况下,应用程序无需调用
begin_access
和end_access
。但这样做并没有错,只是没有效果。 - 应用程序可以通过按任意顺序在每个DataReader上调用
read
或take
方法来访问数据 - 应用程序仍然可以调用
get_datareaders
来确定哪些readers有数据可读,但他不需要读取所有DataReaders,也不需要按特定顺序读取,此外,get_datareaders
的返回值逻辑上是一个集合,这意味着相同的readers不会出现两次,并且返回的readers顺序未作规定。
- 如果应用程序在
SubscriberListener
中访问数据,则采用专门的模式。这种情况不管PRESENTATION QoS如何,都适用于在listener 的on_data_on_readers
内部访问数据
- 类似于上述情况2,应用程序不需要调用
begin_access
和end_access
,即使调用了也不会产生影响 - 应用程序可以按自己期望的顺序通过每个DataReader的
read
或take
方法访问数据 - 应用程序还可以通过调用
notify_datareaders
方法将数据的访问委托给每个 DataReader 的 DataReaderListener 对象。 - 同样地,应用程序仍然可以调用 get_datareaders 来确定哪些 DataReader 有可读的数据,但它不需要读取所有的 DataReader,也不需要按照特定的顺序读取。此外,
get_datareaders
的返回结果逻辑上是一个集合。