pub struct JoinHandle<T> { /* 私有字段 */ }展开描述
用于加入任务(等待其终止)的所有权权限。
这可以看作是 Tokio 任务的 std::thread::JoinHandle 等价物,而不是线程的等价物。请注意,与此 JoinHandle 关联的后台任务在你调用 spawn 时立即开始运行,即使你尚未 await 此 JoinHandle。
当 JoinHandle 被 drop 时,它会分离关联的任务,这意味着不再有该任务的句柄,也无法再 join 它。
此 struct 由 task::spawn 和 task::spawn_blocking 函数创建。
保证在通过 JoinHandle await、JoinHandle::is_finished 或 AbortHandle::is_finished 观察到任务完成之前,已生成任务的析构函数已经运行完毕。
§取消安全性
&mut JoinHandle<T> 类型是取消安全的。如果在 tokio::select! 语句中将其用作事件,并且其他某个分支先完成,则可以保证任务的输出不会丢失。
如果 JoinHandle 被 drop,则任务将继续在后台运行,并且其返回值将丢失。
§示例
从 task::spawn 创建:
use tokio::task;
let join_handle: task::JoinHandle<_> = task::spawn(async {
// some work here
});从 task::spawn_blocking 创建:
use tokio::task;
let join_handle: task::JoinHandle<_> = task::spawn_blocking(|| {
// some blocking work here
});JoinHandle<T> 中的泛型参数 T 是已生成任务的返回类型。如果返回值是 i32,则 join handle 的类型为 JoinHandle<i32>:
use tokio::task;
let join_handle: task::JoinHandle<i32> = task::spawn(async {
5 + 3
});
如果任务没有返回值,则 join handle 的类型为 JoinHandle<()>:
use tokio::task;
let join_handle: task::JoinHandle<()> = task::spawn(async {
println!("I return nothing.");
});请注意,handle.await 不会直接给出返回类型。它被包装在 Result 中,因为已生成任务中的 panic 会被 Tokio 捕获。必须双重链接 ? 运算符才能提取返回值:
use tokio::task;
use std::io;
let join_handle: task::JoinHandle<Result<i32, io::Error>> = tokio::spawn(async {
Ok(5 + 3)
});
let result = join_handle.await??;
assert_eq!(result, 8);
Ok(())如果任务发生 panic,则错误是包含该 panic 的 JoinError:
use tokio::task;
use std::io;
use std::panic;
#[tokio::main]
async fn main() -> io::Result<()> {
let join_handle: task::JoinHandle<Result<i32, io::Error>> = tokio::spawn(async {
panic!("boom");
});
let err = join_handle.await.unwrap_err();
assert!(err.is_panic());
Ok(())
}子任务被分离并比其父任务活得更久:
use tokio::task;
use tokio::time;
use std::time::Duration;
let original_task = task::spawn(async {
let _detached_task = task::spawn(async {
// Here we sleep to make sure that the first task returns before.
time::sleep(Duration::from_millis(10)).await;
// This will be called, even though the JoinHandle is dropped.
println!("♫ Still alive ♫");
});
});
original_task.await.expect("The task being joined has panicked");
println!("Original task is joined.");
// We make sure that the new task has time to run, before the main
// task returns.
time::sleep(Duration::from_millis(1000)).await;实现§
源代码§impl<T> JoinHandle<T>
impl<T> JoinHandle<T>
源代码pub fn abort(&self)
pub fn abort(&self)
中止与该句柄关联的任务。
await 一个已取消的任务可能会像往常一样完成(如果任务在被取消时已经完成),但最有可能的是失败并返回已取消的 JoinError。
请注意,使用 spawn_blocking 派生的任务无法被中止,因为它们不是异步的。如果对 spawn_blocking 任务调用 abort,则不会产生任何效果,任务将继续正常运行。例外情况是任务尚未开始运行;此时调用 abort 可能会阻止任务启动。
有关取消的更多信息,另请参阅 模块级文档。
use tokio::time;
let mut handles = Vec::new();
handles.push(tokio::spawn(async {
time::sleep(time::Duration::from_secs(10)).await;
true
}));
handles.push(tokio::spawn(async {
time::sleep(time::Duration::from_secs(10)).await;
false
}));
for handle in &handles {
handle.abort();
}
for handle in handles {
assert!(handle.await.unwrap_err().is_cancelled());
}源代码pub fn is_finished(&self) -> bool
pub fn is_finished(&self) -> bool
检查与此 JoinHandle 关联的任务是否已完成。
请注意,即使已对任务调用 abort,此方法也可能返回 false。这是因为取消过程可能需要一些时间,并且此方法在取消完成之前不会返回 true。
use tokio::time;
let handle1 = tokio::spawn(async {
// do some stuff here
});
let handle2 = tokio::spawn(async {
// do some other stuff here
time::sleep(time::Duration::from_secs(10)).await;
});
// Wait for the task to finish
handle2.abort();
time::sleep(time::Duration::from_secs(1)).await;
assert!(handle1.is_finished());
assert!(handle2.is_finished());源代码pub fn abort_handle(&self) -> AbortHandle
pub fn abort_handle(&self) -> AbortHandle
返回一个新的 AbortHandle,可用于远程中止此任务。
await 由 AbortHandle 取消的任务,如果该任务在被取消时已经完成,则可能照常完成,但很可能以 已取消 的 JoinError 失败。
use tokio::{time, task};
let mut handles = Vec::new();
handles.push(tokio::spawn(async {
time::sleep(time::Duration::from_secs(10)).await;
true
}));
handles.push(tokio::spawn(async {
time::sleep(time::Duration::from_secs(10)).await;
false
}));
let abort_handles: Vec<task::AbortHandle> = handles.iter().map(|h| h.abort_handle()).collect();
for handle in abort_handles {
handle.abort();
}
for handle in handles {
assert!(handle.await.unwrap_err().is_cancelled());
}