選取
選取作業會等到任何一組 Future 準備就緒,再針對該 Future 的結果提供回應。這類似於 JavaScript 中的 Promise.race。在 Python 中,則可與 asyncio.wait(task_set, return_when=asyncio.FIRST_COMPLETED) 比較。
類似於比對陳述式,select! 的主體有多個分支,格式皆為 pattern = future => statement。當 future 準備就緒時,回傳值會由 pattern 解構。接著,statement 執行時會利用產生的變數。statement 結果會成為 select! 巨集的結果。
use tokio::sync::mpsc::{self, Receiver};
use tokio::time::{sleep, Duration};
#[derive(Debug, PartialEq)]
enum Animal {
Cat { name: String },
Dog { name: String },
}
async fn first_animal_to_finish_race(
mut cat_rcv: Receiver<String>,
mut dog_rcv: Receiver<String>,
) -> Option<Animal> {
tokio::select! {
cat_name = cat_rcv.recv() => Some(Animal::Cat { name: cat_name? }),
dog_name = dog_rcv.recv() => Some(Animal::Dog { name: dog_name? })
}
}
#[tokio::main]
async fn main() {
let (cat_sender, cat_receiver) = mpsc::channel(32);
let (dog_sender, dog_receiver) = mpsc::channel(32);
tokio::spawn(async move {
sleep(Duration::from_millis(500)).await;
cat_sender.send(String::from("Felix")).await.expect("Failed to send cat.");
});
tokio::spawn(async move {
sleep(Duration::from_millis(50)).await;
dog_sender.send(String::from("Rex")).await.expect("Failed to send dog.");
});
let winner = first_animal_to_finish_race(cat_receiver, dog_receiver)
.await
.expect("Failed to receive winner");
println!("Winner is {winner:?}");
}
-
這個範例是貓狗賽跑。
first_animal_to_finish_race會監聽兩個管道,並挑選先抵達者。狗花了 50 毫秒,因此贏過花費 500 毫秒的貓。 -
在這個範例中,您可以使用
oneshot管道,因為管道只應接收一個send。 -
嘗試為賽跑加上期限,示範選擇各種 Future 的情形。
-
請注意,
select!會捨棄不相符的分支版本,這會取消 Future。若每次執行select!時都會建立新的 Future,就最容易使用這種做法。- 替代方法是傳遞
&mut future,而非傳遞 Future 本身,但這可能導致多項問題,詳細討論請見 pinning 投影片。
- 替代方法是傳遞