1 个不稳定版本
0.1.0 | 2024 年 5 月 5 日 |
---|
2032 在 嵌入式开发 中
180KB
1.5K SLoC
为 lilos
提供低成本的价值共享和变更通知
该软件包提供了 Watch<T>
数据结构,用于带有更新通知的数据共享。有关详细信息,请参阅软件包文档。
lib.rs
:
一个简单的机制,用于共享一块数据并在它发生变化时接收通知。
此模块提供了 Watch<T>
,这是一个数据结构,允许您共享类型为 T
的一些数据,从多个生产者那里更新它,并在它发生变化时高效地通知多个消费者。
Watch<T>
对于配置数据等特别有用,这些数据实际上是“全局”的(由许多任务共享),但可能需要特殊处理变更。
let shared_number = Watch::new(1234_u32);
您可以通过调用 subscribe
创建一个对 Watch<T>
的 接收句柄。这将生成一个 Receiver<T>
,允许其持有者检查共享数据,并在它发生变化时接收通知。
let rcvr = shared_number.subscribe();
// A receiver only tracks changes made _after_ it's created:
assert_eq!(rcvr.is_changed(), false);
您可以通过调用 sender
创建一个对 Watch<T>
的 发送句柄。这将生成一个 Sender<T>
,允许其持有者更新共享数据。
let sender = shared_number.sender();
// Update the shared data:
sender.send(4567);
// Now the receiver sees a change:
assert_eq!(rcvr.is_changed(), true);
// We can inspect it and mark it as seen:
rcvr.glimpse_and_update(|value| assert_eq!(*value, 4567));
希望监控数据变更的代码可以使用 Receiver::changed
future 来实现
loop {
rcvr.changed().await;
rcvr.glimpse_and_update(|value| process(value));
}
重入性和恐慌
如果努力足够,可以尝试重新进入使用单个Watch<T>
的句柄。实现会检查这种情况并引发恐慌。例如,尝试从传递给Receiver::glimpse
的闭包的内部发送新值。
let shared = Watch::new(some_data);
let sender = shared.sender();
let rcvr = shared.subscribe();
// This line will panic at runtime.
rcvr.glimpse(|contents| sender.send(*contents));
在其中一个闭包内部发送或检查一个不同的 Watch<T>
实例是绝对安全的,只是不能是同一个。
实际上,这样做几乎是不可能的,但现在你知道了。
实现
具体来说,Watch<T>
包含一个更改计数。每次其内容通过任何Sender
更新时,更改计数都会增加。
每个Receiver
都有一个更改计数的副本,反映了它在最后一次访问共享数据时的计数。如果存储在Watch
中的更改计数与存储在Receiver
中的不同,那么有一个更新还没有被其所有者看到。
由于Watch<T>
只存储数据的单个副本和一个计数器,因此Receiver
可能无法看到数据的每个更新。如果在检查之间发生多个更新,则Receiver
将始终只看到最后一个更新。这既降低了存储要求,也降低了更新的成本。
Watch<T>
内部包含一个Notify
,它使用它来有效地唤醒正在等待Receiver::changed
的任务。
依赖项
~2MB
~42K SLoC