pub struct Notify { /* private fields */ }展开描述
通知单个 task 唤醒。
提供了
将事件
通知给
单个任务的
基本机制。
Notify
本身
不携带任何数据。
相反,
它用于
通知
另一个任务
执行
某个操作。Notify
可以将
视为
从 0 个 permit
开始的
Notify。
Semaphore
方法
等待
permit 变为可用,
而
notified().await
会
如果当前没有可用的
permit
设置
一个 permit。notify_one()
的
同步细节
类似于
std 中的
Notify
和
thread::park。
Thread::unpark
值
包含
单个 permit。
Notify
等待
permit
变为可用,
消费
该 permit,
然后恢复。
notified().await
设置
该 permit,
如果存在
挂起的任务,
则唤醒它。notify_one()
如果
被调用
在
notify_one()
之前,
那么
下一次
对
notified().await
的调用
将立即完成,
消耗
该 permit。
之后
任何对
notified().await
的调用
将
等待
新的 permit。notified().await
如果
在
notify_one()
之前
被调用
多次,
则
仅存储
一个
permit。
下一次
对
notified().await
的调用
将
立即完成,
但
之后
将
等待
新的 permit。notified().await
§示例
基本用法。
use tokio::sync::Notify;
use std::sync::Arc;
let notify = Arc::new(Notify::new());
let notify2 = notify.clone();
let handle = tokio::spawn(async move {
notify2.notified().await;
println!("received notification");
});
println!("sending notification");
notify.notify_one();
// Wait for task to receive notification.
handle.await.unwrap();无界 多生产者单消费者 (mpsc) 通道。
使用
此通道时,
不会丢失
任何唤醒,
因为
对
的调用
会在
notify_one()
中存储一个 permit,
随后的
对
Notify
的调用
会
消费该 permit。notified()
use tokio::sync::Notify;
use std::collections::VecDeque;
use std::sync::Mutex;
struct Channel<T> {
values: Mutex<VecDeque<T>>,
notify: Notify,
}
impl<T> Channel<T> {
pub fn send(&self, value: T) {
self.values.lock().unwrap()
.push_back(value);
// Notify the consumer a value is available
self.notify.notify_one();
}
// This is a single-consumer channel, so several concurrent calls to
// `recv` are not allowed.
pub async fn recv(&self) -> T {
loop {
// Drain values
if let Some(value) = self.values.lock().unwrap().pop_front() {
return value;
}
// Wait for values to be available
self.notify.notified().await;
}
}
}无界 多生产者多消费者 (mpmc) 通道。
调用
很重要,
因为
否则
如果你
并行地
有两个
对
enable
的调用
和
两个
对
recv
的调用,
可能会发生
以下情况:send
- Both calls to
try_recvreturnNone. - Both new elements are added to the vector.
- The
notify_onemethod is called twice, adding only a single permit to theNotify. - Both calls to
recvreach theNotifiedfuture. One of them consumes the permit, and the other sleeps forever.
通过在
之前调用
try_recv
将
enable
future
添加到列表中,
步骤三中的
Notified
调用
会
从列表中移除
这些 future,
并将它们
标记为已通知,
而不是
向
notify_one
添加 permit。
这
确保
两个 future
都会被唤醒。Notify
请注意,
此失败
仅在
有两个
对
的并发调用时
才会发生。
这就是
上面的 mpsc 示例
不需要调用
recv
的原因。enable
use tokio::sync::Notify;
use std::collections::VecDeque;
use std::sync::Mutex;
struct Channel<T> {
messages: Mutex<VecDeque<T>>,
notify_on_sent: Notify,
}
impl<T> Channel<T> {
pub fn send(&self, msg: T) {
let mut locked_queue = self.messages.lock().unwrap();
locked_queue.push_back(msg);
drop(locked_queue);
// Send a notification to one of the calls currently
// waiting in a call to `recv`.
self.notify_on_sent.notify_one();
}
pub fn try_recv(&self) -> Option<T> {
let mut locked_queue = self.messages.lock().unwrap();
locked_queue.pop_front()
}
pub async fn recv(&self) -> T {
let future = self.notify_on_sent.notified();
tokio::pin!(future);
loop {
// Make sure that no wakeup is lost if we get
// `None` from `try_recv`.
future.as_mut().enable();
if let Some(msg) = self.try_recv() {
return msg;
}
// Wait for a call to `notify_one`.
//
// This uses `.as_mut()` to avoid consuming the future,
// which lets us call `Pin::set` below.
future.as_mut().await;
// Reset the future in case another call to
// `try_recv` got the message before us.
future.set(self.notify_on_sent.notified());
}
}
}实现§
Source§impl Notify
impl Notify
Sourcepub const fn const_new() -> Notify
pub const fn const_new() -> Notify
创建一个新的 Notify,初始化时没有许可证。
使用 tracing 不稳定特性时,通过 const_new 创建的 Notify 不会被插桩。因此,它不会出现在 tokio-console 中。如有需要,请改用 Notify::new 来创建可插桩的对象。
§示例
use tokio::sync::Notify;
static NOTIFY: Notify = Notify::const_new();Sourcepub fn notified(&self) -> Notified<'_> ⓘ
pub fn notified(&self) -> Notified<'_> ⓘ
等待通知。
等价于:
async fn notified(&self);每个 Notify 值持有一个许可证。如果之前调用 notify_one() 后还有可用的许可证,那么 notified().await 会立即完成并消费该许可证。否则,notified().await 等待下一次调用 notify_one() 来提供许可证。
如果 Notified future 还未被 poll,则不能保证它能收到 notify_one() 调用的唤醒。详见 Notified::enable() 的文档。
Notified future 一旦被创建就能保证收到 notify_waiters() 的唤醒,即使它还未被 poll。
§Cancel safety
此方法使用队列按请求顺序公平分发通知。取消对 notified 的调用会丢失在队列中的位置。
§示例
use tokio::sync::Notify;
use std::sync::Arc;
let notify = Arc::new(Notify::new());
let notify2 = notify.clone();
tokio::spawn(async move {
notify2.notified().await;
println!("received notification");
});
println!("sending notification");
notify.notify_one();Sourcepub fn notified_owned(self: Arc<Self>) -> OwnedNotified ⓘ
pub fn notified_owned(self: Arc<Self>) -> OwnedNotified ⓘ
使用拥有的 Future 等待通知。
与 Self::notified 返回绑定到 Notify 生命周期的 future 不同,notified_owned 创建一个独立的 future,它拥有自己的通知状态,因此可以安全地在线程间移动。
详见 Self::notified。
§Cancel safety
此方法使用队列按请求顺序公平分发通知。取消对 notified_owned 的调用会丢失在队列中的位置。
§示例
use std::sync::Arc;
use tokio::sync::Notify;
let notify = Arc::new(Notify::new());
for _ in 0..10 {
let notified = notify.clone().notified_owned();
tokio::spawn(async move {
notified.await;
println!("received notification");
});
}
println!("sending notification");
notify.notify_waiters();Sourcepub fn notify_one(&self)
pub fn notify_one(&self)
通知第一个等待的任务。
如果当前有任务正在等待,该任务将被通知。否则,一个许可证会存储到此 Notify 值中,下一次调用 notified().await 将立即完成并消费本次 notify_one() 调用所提供的许可证。
Notify 最多只能存储一个许可证。多次连续调用 notify_one 只会存储一个许可证。下一次调用 notified().await 会立即完成,但再下一次则会等待。
§示例
use tokio::sync::Notify;
use std::sync::Arc;
let notify = Arc::new(Notify::new());
let notify2 = notify.clone();
tokio::spawn(async move {
notify2.notified().await;
println!("received notification");
});
println!("sending notification");
notify.notify_one();Sourcepub fn notify_last(&self)
pub fn notify_last(&self)
通知最后一个等待的任务。
此函数行为与 notify_one 类似。唯一区别是它唤醒最近添加的等待者而不是最早添加的。
请参阅 notify_one() 的文档以获取更多信息和示例。
Sourcepub fn notify_waiters(&self)
pub fn notify_waiters(&self)
通知所有等待的任务。
如果当前有任务正在等待,该任务将被通知。与 notify_one() 不同的是,此方法不会存储许可证供下一次 notified().await 使用。此方法的目的是通知所有已注册的等待者。注册的通过调用 notified() 获取 Notified future 实例完成。
§示例
use tokio::sync::Notify;
use std::sync::Arc;
let notify = Arc::new(Notify::new());
let notify2 = notify.clone();
let notified1 = notify.notified();
let notified2 = notify.notified();
let handle = tokio::spawn(async move {
println!("sending notifications");
notify2.notify_waiters();
});
notified1.await;
notified2.await;
println!("received notifications");