tokio::try_join!当其中一项任务 return 出错时,return 不是 Err 变体吗?
tokio::try_join! doesn't return the Err variant when one of the tasks returns Err?
我无法理解 tokio::try_run!
和任务 运行ning 在 tokio::spawn
returning 和 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);
}
}
}
它打印
h2 didn't get canceled
try_join was successful, got Err(()) and Ok(2)
但是,我希望它打印出类似我在 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(...)
try_join!
文档说
The try_join! macro returns when all branches return with Ok or when the first branch returns with Err.
但是,在我发布的示例中,h1 执行 return Err
但 try_join!
执行 Ok
变体., 此外,h2 不会被取消,它 运行s 完成,即使 h1 已经失败了数百毫秒。我不明白这是否与文档相矛盾。另外,我似乎无法实现我想要做的事情,即在 h1 returns Err
.
时取消 h2
经过反复试验,我发现当我从 h1 和 h2 中删除 tokio::spawn
时,try_join!
确实按照我预期的方式执行并调用了 Err
变体.不过,我不明白为什么这会有所不同。
任何人都可以提供更多信息来解释为什么会出现这种行为吗?如果我希望在 h1 return 出错时取消 h2,是否需要删除 tokio::spawn
并放弃 h1 和 h2 之间的并行执行?
首先你必须了解期货是如何运作的。 rust async book 是一个很好的起点。
与自行取得进展的线程不同,未来必须轮询。如果它没有被轮询,它不会做任何事情。所以有两种方法可以做到这一点:
作为另一个异步函数的一部分:
async fn foo(){
// do something
}
async fn bar(){
foo().await; // here foo() is being polled
}
这种方法的问题是需要有人推动未来。这里 bar()
正在驾驶 foo()
,但它不会做任何事情,除非有人 驾驶 bar()
-(即调用它的 poll()
方法)
生成任务
你可以用spawn()
的方法把轮询未来的责任交给运行时间。当你这样做时,你不再需要(也不能)在 future 上调用 .await
了。现在任务计划程序将为您完成。
回到问题
那么为什么它对你的情况不起作用?
let h1 = tokio::spawn(async {...});
let h2 = tokio::spawn(async {...});
它不起作用,因为您正在 生成 任务。把它想象成你正在启动两个彼此独立工作的线程(尽管你不是)。您不再负责轮询期货 - 运行时间将为您完成。这两个任务将 运行 完成,无论它们的连接句柄是否正在被轮询。
我猜你的困惑来自连接句柄 h1
和 h2
- 是的 - 你可以 .await
这些,但它们只能告诉你任务是否完成- 他们将 而不是 驱动实际任务 - tokio 调度程序会。您可以将它们想象成 thread
的连接句柄 - 无论您是否 .join()
线程都没有关系 - 它仍然会在后台 运行。这就是 h2
仍然 运行 完成的原因 - 因为任务仍在由调度程序轮询 - try_join!()
宏没有驱动任务。
当您不生成它们时,try_join!()
是 驱动任务。它是在实际的未来调用.poll()
,所以当task-1完成时,它停止在task-2上调用.poll()
,从而有效地取消它。
TLDR:生成时,try_join!()
驱动连接手柄,而在另一种情况下它驱动期货本身。
你的另一个问题
Do I need to remove tokio::spawn
and forfeit parallel execution between h1
and h2
if I want h2
to be canceled when h1 returns an error?
否 - 您可以使用 JoinHandle::abort()
手动取消第二个任务
在评论中回答你的问题:
Now, this raises a second question (and I think the source of my confusion): even when using tokio::spawn, select! does cancel h2 (i.e. no need for abort(), and h2 doesn't print the h2 didn't get canceled line). This seems to be what's weird to me: while select and join seem kind of similar, their behavior is the opposite.
这里的问题是您的应用程序到达了 main()
的末尾,因此您的整个 运行 时间都停止了,一切都被取消了。如果您在末尾添加一个简短的 sleep()
,您将看到您的消息:
tokio::select! {
_ = h1 => println!("H1"),
_ = h2 => println!("H2"),
}
sleep(Duration::from_secs(2)).await;
这导致:
H1
h2 didn't get canceled
Process finished with exit code 0
我无法理解 tokio::try_run!
和任务 运行ning 在 tokio::spawn
returning 和 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);
}
}
}
它打印
h2 didn't get canceled
try_join was successful, got Err(()) and Ok(2)
但是,我希望它打印出类似我在 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(...)
try_join!
文档说
The try_join! macro returns when all branches return with Ok or when the first branch returns with Err.
但是,在我发布的示例中,h1 执行 return Err
但 try_join!
执行 Ok
变体., 此外,h2 不会被取消,它 运行s 完成,即使 h1 已经失败了数百毫秒。我不明白这是否与文档相矛盾。另外,我似乎无法实现我想要做的事情,即在 h1 returns Err
.
经过反复试验,我发现当我从 h1 和 h2 中删除 tokio::spawn
时,try_join!
确实按照我预期的方式执行并调用了 Err
变体.不过,我不明白为什么这会有所不同。
任何人都可以提供更多信息来解释为什么会出现这种行为吗?如果我希望在 h1 return 出错时取消 h2,是否需要删除 tokio::spawn
并放弃 h1 和 h2 之间的并行执行?
首先你必须了解期货是如何运作的。 rust async book 是一个很好的起点。
与自行取得进展的线程不同,未来必须轮询。如果它没有被轮询,它不会做任何事情。所以有两种方法可以做到这一点:
作为另一个异步函数的一部分:
async fn foo(){
// do something
}
async fn bar(){
foo().await; // here foo() is being polled
}
这种方法的问题是需要有人推动未来。这里 bar()
正在驾驶 foo()
,但它不会做任何事情,除非有人 驾驶 bar()
-(即调用它的 poll()
方法)
生成任务
你可以用spawn()
的方法把轮询未来的责任交给运行时间。当你这样做时,你不再需要(也不能)在 future 上调用 .await
了。现在任务计划程序将为您完成。
回到问题
那么为什么它对你的情况不起作用?
let h1 = tokio::spawn(async {...});
let h2 = tokio::spawn(async {...});
它不起作用,因为您正在 生成 任务。把它想象成你正在启动两个彼此独立工作的线程(尽管你不是)。您不再负责轮询期货 - 运行时间将为您完成。这两个任务将 运行 完成,无论它们的连接句柄是否正在被轮询。
我猜你的困惑来自连接句柄 h1
和 h2
- 是的 - 你可以 .await
这些,但它们只能告诉你任务是否完成- 他们将 而不是 驱动实际任务 - tokio 调度程序会。您可以将它们想象成 thread
的连接句柄 - 无论您是否 .join()
线程都没有关系 - 它仍然会在后台 运行。这就是 h2
仍然 运行 完成的原因 - 因为任务仍在由调度程序轮询 - try_join!()
宏没有驱动任务。
当您不生成它们时,try_join!()
是 驱动任务。它是在实际的未来调用.poll()
,所以当task-1完成时,它停止在task-2上调用.poll()
,从而有效地取消它。
TLDR:生成时,try_join!()
驱动连接手柄,而在另一种情况下它驱动期货本身。
你的另一个问题
Do I need to remove
tokio::spawn
and forfeit parallel execution betweenh1
andh2
if I wanth2
to be canceled when h1 returns an error?
否 - 您可以使用 JoinHandle::abort()
手动取消第二个任务
在评论中回答你的问题:
Now, this raises a second question (and I think the source of my confusion): even when using tokio::spawn, select! does cancel h2 (i.e. no need for abort(), and h2 doesn't print the h2 didn't get canceled line). This seems to be what's weird to me: while select and join seem kind of similar, their behavior is the opposite.
这里的问题是您的应用程序到达了 main()
的末尾,因此您的整个 运行 时间都停止了,一切都被取消了。如果您在末尾添加一个简短的 sleep()
,您将看到您的消息:
tokio::select! {
_ = h1 => println!("H1"),
_ = h2 => println!("H2"),
}
sleep(Duration::from_secs(2)).await;
这导致:
H1
h2 didn't get canceled
Process finished with exit code 0