6 个版本
0.0.10 | 2019 年 9 月 14 日 |
---|---|
0.0.9 | 2019 年 8 月 19 日 |
#668 in 并发
在 2 crates 中使用
74KB
1K SLoC
Rust 的跳转启用并发通道 (SECC)
描述
跳转启用并发通道 (SECC) 是一个具有固定容量的通道,支持多个发送者和多个接收者,允许接收者根据需要暂时跳过接收消息。
通道中的消息需要是可克隆的,以实现 peek
功能(该功能返回消息的一个克隆)。因此,建议用户选择一个高效可克隆的类型,例如使用 [Arc
] 封装无法高效克隆的消息。
除非用户打算跳过一个或多个消息,否则通道是一个 FIFO 结构。在这种情况下,消息的读取顺序可能会不同。然而,通道保证消息将保持与发送时相同的顺序,除非跳过,否则将按顺序接收。
SECC 使用两个链表实现,其中一个列表作为节点池,另一个列表作为包含消息的队列。这使得我们能够以 O(1) 的效率在列表中移动节点,甚至可以跳过一个消息。如果有 1000 条消息,用户想要跳过中间的一条,那么他们将承担与正常读取操作几乎相同的性能成本。只需要进行几个额外的指针操作就可以从中间的链表节点中移除节点,这实现了队列。当从通道接收到消息时,包含消息的节点将从队列中移除并附加到池的尾部。相反,当向通道发送消息时,节点将从池的头部移动到队列的尾部。通过这种方式,节点不断地在队列中进出,因此我们只需在创建通道时分配一次。
示例
use secc::*;
use std::time::Duration;
let channel = create::<u8>(5, Duration::from_millis(10));
let (sender, receiver) = channel;
assert_eq!(Ok(()), sender.send(17));
assert_eq!(Ok(()), sender.send(19));
assert_eq!(Ok(()), sender.send(23));
assert_eq!(Ok(()), sender.send(29));
assert_eq!(Ok(17), receiver.receive());
assert_eq!(Ok(()), receiver.skip());
assert_eq!(Ok(23), receiver.receive());
assert_eq!(Ok(()), receiver.reset_skip());
assert_eq!(Ok(19), receiver.receive());
此代码创建了一个通道并发送了一系列消息。第一条消息正常接收,然后用户想要跳过下一条消息。然后用户可以在通道的中间接收,重置跳过并继续正常接收。
更新内容
- 2019-09-13: 0.0.10
- 问题 #13:如果在等待空间或数据时超时,则会出现死锁。
- 重大更改:超时现在在
Duration
对象中,而不是以毫秒为单位。
- 2019-08-18: 0.0.9
- 大多数
不安全
代码已被消除,增强了稳定性。
- 大多数
设计原则
SECC是由对多发送者、多消费者通道的需求驱动的,这种通道能够跳过处理消息。有很多情况下消费者需要这种情况,例如与Axiom一起使用的情况,其中actor实现了一个有限状态机。这导致我经历了许多不同设计的迭代,直到很明显地认识到链表是唯一合法的方法。链表的问题在于它们通常在每个入队操作中会消耗大量的CPU时间来分配新节点。解决方案是使用两个链表,提前分配所有节点,只需在逻辑上移动节点。实际指向下一个节点或各种头部和尾部的指针是静态分配的节点切片中的索引。当发送和接收操作发生时,节点只是在逻辑上移动,而不会在物理上移动。