jgp*_*iva 4 rust async-await rust-tokio
我无法理解tokio::try_run!
返回tokio::spawn
. Err
当我运行以下示例时:
use tokio::time::{sleep, Duration};
#[tokio::main]
async fn main() {
let h1 = tokio::spawn(async {
sleep(Duration::from_millis(100)).await;
// 1/0; commented for now
let v: Result<i32, ()> = Err(());
v
});
let h2 = tokio::spawn(async {
sleep(Duration::from_millis(500)).await;
println!("h2 didn't get canceled");
let v: Result<i32, ()> = Ok(2);
v
});
match tokio::try_join!(h1, h2) {
Ok((first, second)) => {
println!("try_join was successful, got {:?} and {:?}", first, second);
}
Err(err) => {
println!("try_join had an error: {:?}", err);
}
}
}
Run Code Online (Sandbox Code Playgroud)
它打印
h2 didn't get canceled
try_join was successful, got Err(()) and Ok(2)
Run Code Online (Sandbox Code Playgroud)
但是,我希望它打印出类似于我在 h1 中取消除以零的注释时发生的情况:
thread 'tokio-runtime-worker' panicked at 'attempt to divide by zero', src/bin/select-test.rs:7:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
try_join had an error: JoinError::Panic(...)
Run Code Online (Sandbox Code Playgroud)
文档try_join!
说
尝试加入!当所有分支返回 Ok 或第一个分支返回 Err 时,宏返回。
然而,在我发布的示例中,h1确实返回Err
但try_join!
执行了Ok
变体。此外,h2 不会被取消,它会运行到完成,即使 h1 之前已经失败了数百毫秒。我不明白这是否与文档相矛盾。另外,我似乎无法实现我想要做的事情,即当 h1 返回时取消 h2 Err
。
经过更多的试验和错误,我发现当我tokio::spawn
从 h1 和 h2 中删除 时,try_join!
确实以我预期的方式执行并调用Err
变体。尽管如此,我还是不明白为什么这会产生影响。
任何人都可以提供更多信息来解释为什么会出现这种行为吗?tokio::spawn
如果我希望在 h1 返回错误时取消 h2,是否需要删除并放弃 h1 和 h2 之间的并行执行?
首先你必须了解期货是如何运作的。Rust异步书是一个很好的起点。
与自行取得进展的线程不同,必须对 future 进行轮询。如果不进行轮询,它将不会执行任何操作。所以有两种方法可以做到这一点:
async fn foo(){
// do something
}
async fn bar(){
foo().await; // here foo() is being polled
}
Run Code Online (Sandbox Code Playgroud)
这种方法的问题在于需要有人来推动未来。这里bar()
是 Driving ,但除非有人开车foo()
,否则它不会做任何事情- (即调用它的方法) bar()
poll()
您可以使用该spawn()
方法将轮询未来的责任移交给运行时。当你这样做时,你不再需要(也不能)再呼唤.await
未来。现在任务计划程序将为您完成此操作。
那么为什么它在你的情况下不起作用呢?
let h1 = tokio::spawn(async {...});
let h2 = tokio::spawn(async {...});
Run Code Online (Sandbox Code Playgroud)
它不起作用,因为您正在生成任务。可以将其想象为您正在启动两个彼此独立工作的线程(尽管实际上不是)。您不再负责轮询 future - 运行时将为您做这件事。无论它们的连接句柄是否被轮询,这两个任务都将运行完成。
我猜你的困惑来自于连接句柄h1
-h2
是的 - 你可以.await
这些,但它们只能告诉你任务是否完成 - 它们不会驱动实际的任务 - tokio 调度程序会。您可以将它们想象为 a 的连接句柄thread
- 如果您这样做并不重要。join()
线程与否 - 它仍然会在后台运行。这就是为什么h2
仍然运行到完成 - 因为任务仍在由调度程序轮询 -try_join!()
宏没有驱动任务。
当你不生成它们时,它们try_join!()
就会驱动任务。它正在调用.poll()
实际的 future,因此当任务 1 完成时,它会停止调用.poll()
任务 2,从而有效地取消它。
TLDR:生成时,try_join!()
正在驱动连接句柄,而在其他情况下,它正在驱动期货本身。
如果我想在 h1 返回错误时取消,是否需要删除
tokio::spawn
并放弃之间的并行执行?h1
h2
h2
否 - 您可以用来JoinHandle::abort()
手动取消第二个任务
现在,这提出了第二个问题(我认为这是我困惑的根源):即使使用 tokio::spawn 时,也要选择!确实取消 h2 (即不需要 abort(),并且 h2 不会打印 h2 没有被取消行)。这对我来说似乎很奇怪:虽然 select 和 join 看起来有点相似,但它们的行为却相反。
这里的问题是您的应用程序到达了 的末尾main()
,因此您的整个运行时将停止并且所有内容都被取消。sleep()
如果您在末尾添加简介,您将看到您的消息:
tokio::select! {
_ = h1 => println!("H1"),
_ = h2 => println!("H2"),
}
sleep(Duration::from_secs(2)).await;
Run Code Online (Sandbox Code Playgroud)
结果是:
H1
h2 didn't get canceled
Process finished with exit code 0
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
1562 次 |
最近记录: |