Структура Join Handle
pub struct JoinHandle<T> { /* private fields */ }
rt.Владеющее разрешение на присоединение к задаче (ожидание её завершения).
Это можно считать эквивалентом std::thread::JoinHandle для задачи Tokio, а не потока. Обратите внимание, что фоновая задача, связанная с этим JoinHandle, начинает выполняться сразу после вызова spawn, даже если вы ещё не ожидали JoinHandle.
При удалении JoinHandle связанная задача отсоединяется, то есть дескриптора этой задачи больше не существует и её нельзя join.
Этот struct создаётся функциями task::spawn и task::spawn_blocking.
Гарантируется, что деструктор запущенной задачи завершит работу до того, как завершение задачи будет обнаружено через JoinHandle await, JoinHandle::is_finished или AbortHandle::is_finished.
Безопасность при отмене
Ожидание &mut JoinHandle<T> безопасно при отмене. Если оно используется в качестве ветви в tokio::select! и первой завершается другая ветвь, гарантируется, что результат задачи не будет потерян.
Если JoinHandle удаляется, задача продолжает выполняться в фоновом режиме, а её возвращаемое значение теряется.
Примеры
Создание с помощью 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
});Параметр типа T в JoinHandle<T> — это тип возвращаемого значения запущенной задачи. Если возвращаемое значение имеет тип i32, дескриптор присоединения имеет тип JoinHandle<i32>:
use tokio::task;
let join_handle: task::JoinHandle<i32> = task::spawn(async {
5 + 3
});
Если задача не возвращает значение, дескриптор присоединения имеет тип JoinHandle<()>:
use tokio::task;
let join_handle: task::JoinHandle<()> = task::spawn(async {
println!("I return nothing.");
});Обратите внимание, что handle.await не возвращает тип возвращаемого значения напрямую. Оно обёрнуто в Result, поскольку паники в запущенной задаче перехватываются 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(())Если задача завершается паникой, ошибкой будет 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>
pub fn abort(&self)
Прерывает связанную с дескриптором задачу.
Ожидание отменённой задачи может завершиться как обычно, если задача уже завершилась к моменту её отмены, но, скорее всего, оно завершится ошибкой — отменённым JoinError.
Обратите внимание: задачи, запущенные с помощью spawn_blocking, нельзя прервать, поскольку они не являются асинхронными. Если вызвать abort для задачи spawn_blocking, это не окажет никакого эффекта, и задача продолжит выполняться в обычном режиме. Исключение — задача ещё не начала выполняться; в этом случае вызов 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
Проверяет, завершилась ли задача, связанная с этим JoinHandle.
Обратите внимание: этот метод может вернуть false, даже если для задачи был вызван abort. Это связано с тем, что процесс отмены может занять некоторое время, и метод возвращает 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
Возвращает новый AbortHandle, который можно использовать для удалённого прерывания этой задачи.
Ожидание задачи, отменённой с помощью 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());
}pub fn id(&self) -> Id
Возвращает идентификатор задачи, который однозначно отличает эту задачу от других задач, запущенных в данный момент.
Реализации трейтов
impl<T> Debug for JoinHandle<T>where T: Debug,
impl<T> Drop for JoinHandle<T>
impl<T> Future for JoinHandle<T>
impl<T> RefUnwindSafe for JoinHandle<T>
impl<T: Send> Send for JoinHandle<T>
impl<T: Send> Sync for JoinHandle<T>
impl<T> Unpin for JoinHandle<T>
impl<T> UnwindSafe for JoinHandle<T>
Автоматические реализации трейтов
impl<T> Freeze for JoinHandle<T>
impl<T> UnsafeUnpin for JoinHandle<T>
Общие реализации
impl<T> BorrowMut<T> for Twhere T: ?Sized,
fn borrow_mut(&mut self) -> &mut T
impl<T> Instrument for T
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
impl<F> IntoFuture for Fwhere F: Future,
type Output = <F as Future>::Output
type IntoFuture = F
fn into_future(self) -> <F as IntoFuture>::IntoFuture
impl<T> WithSubscriber for T
fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ
Subscriber к этому типу, возвращая обёртку WithDispatch. Подробнее
MIT License
Copyright © Tokio Contributors
https://docs.rs/tokio/1.53.1/tokio/task/struct.JoinHandle.html