我是异步编程的新手,因此努力解决不同方法的行为差异。
考虑 tokio 在 github 存储库 chat.rs 中给出的示例:
// snip
loop {
tokio::select! {
// A message was received from a peer. Send it to the current user.
Some(msg) = peer.rx.recv() => {
// do something
}
result = peer.lines.next() => match result {
// A message was received from the current user, we should
// broadcast this message to the other users.
Some(Ok(msg)) => {
// do something
}
Some(Err(e)) => {
// handle error
}
// The stream has been exhausted.
None => break,
},
}
}
// do something
Run Code Online (Sandbox Code Playgroud)
使用循环select有什么好处!超过两个 tokio::spawn,如下所示:
let handle_1 = tokio::spawn(async move {
while let Some(msg) = peer.rx.recv() {
// do something
}
});
let handle_2 = tokio::spawn (async move {
loop {
let result = peer.lines.next();
match result {
Some(Ok(msg)) => {
// do something
},
Some(Err(e)) => {
// handle error
},
None => break,
};
}
});
handle_1.await;
handle_2.await;
// do something
Run Code Online (Sandbox Code Playgroud)
一般来说,select!效率更高,因为它不需要生成新任务,这非常便宜,但仍然比仅仅轮询 future 更昂贵。但是,有一些注意事项:
如果存在频繁的消息,这使得任务更受 CPU 限制,建议生成一个新任务,因为select!在同一个线程上运行所有 future,而在spawn()多线程运行时可能会使用不同的线程。
中的期货select!应该是取消安全的,这意味着可以安全地删除未完成的期货,并且这不会导致任何数据丢失。如果不是这种情况,则可能存在错误。它使得select!使用任务编程变得更加困难。例如,请参阅这篇文章。