Функция spawn
pub fn spawn<F>(future: F) -> JoinHandle<F::Output> ⓘ
rt.Запускает новую асинхронную задачу и возвращает для неё JoinHandle.
Предоставленный future начнёт выполняться в фоновом режиме сразу после вызова spawn, даже если вы не будете ожидать возвращённый JoinHandle.
Запуск задачи позволяет ей выполняться одновременно с другими задачами. Запущенная задача может выполняться в текущем потоке или быть отправлена для выполнения в другой поток. Конкретное поведение зависит от конфигурации текущего Runtime. В работающей среде выполнения задача сразу начнёт выполняться в фоновом режиме. Если среда выполнения заблокирована, пользователь должен обеспечить её дальнейшую работу (например, вызвав Runtime::block_on).
Гарантируется, что spawn не будет синхронно опрашивать запускаемую задачу. Это означает, что вызов spawn при удержании блокировки не создаёт риска взаимной блокировки с запущенной задачей.
Нет гарантии, что запущенная задача выполнится до конца. При завершении работы среды выполнения все незавершённые задачи отбрасываются независимо от их жизненного цикла.
Эту функцию необходимо вызывать в контексте среды выполнения Tokio. Задачи, выполняющиеся в среде выполнения Tokio, всегда находятся в её контексте, но войти в контекст также можно с помощью метода Runtime::enter.
Примеры
В этом примере запускается сервер, а spawn используется для запуска новой задачи, обрабатывающей каждое принятое соединение.
use tokio::net::{TcpListener, TcpStream};
use std::io;
async fn process(socket: TcpStream) {
// ...
}
#[tokio::main]
async fn main() -> io::Result<()> {
let listener = TcpListener::bind("127.0.0.1:8080").await?;
loop {
let (socket, _) = listener.accept().await?;
tokio::spawn(async move {
// Process each socket concurrently.
process(socket).await
});
}
}Чтобы запустить несколько задач параллельно и получить их результаты, дескрипторы соединения можно сохранить в векторе.
async fn my_background_op(id: i32) -> String {
let s = format!("Starting background task {}.", id);
println!("{}", s);
s
}
let ops = vec![1, 2, 3];
let mut tasks = Vec::with_capacity(ops.len());
for op in ops {
// This call will make them start running in the background
// immediately.
tasks.push(tokio::spawn(my_background_op(op)));
}
let mut outputs = Vec::with_capacity(tasks.len());
for task in tasks {
outputs.push(task.await.unwrap());
}
println!("{:?}", outputs);В этом примере задачи добавляются в outputs в порядке их запуска. Если порядок вывода не важен, можно также использовать JoinSet.
Паники
Вызывает панику, если функция вызвана вне среды выполнения Tokio.
Использование значений !Send в задаче
Задача, передаваемая в spawn, должна реализовывать Send. Однако в задаче можно использовать значения !Send, если они существуют только между вызовами .await.
Например, следующий код будет работать:
use tokio::task;
use std::rc::Rc;
fn use_rc(rc: Rc<()>) {
// Do stuff w/ rc
}
tokio::spawn(async {
// Force the `Rc` to stay in a scope with no `.await`
{
let rc = Rc::new(());
use_rc(rc.clone());
}
task::yield_now().await;
}).await.unwrap();А следующий код не будет работать:
use tokio::task;
use std::rc::Rc;
fn use_rc(rc: Rc<()>) {
// Do stuff w/ rc
}
#[tokio::main]
async fn main() {
tokio::spawn(async {
let rc = Rc::new(());
task::yield_now().await;
use_rc(rc.clone());
}).await.unwrap();
}
Если сохранять значение !Send между вызовами .await, возникнет неудобное сообщение об ошибке компиляции, похожее на:
`[... some type ...]` cannot be sent between threads safelyили:
error[E0391]: cycle detected when processing `main`
MIT License
Copyright © Tokio Contributors
https://docs.rs/tokio/1.53.1/tokio/task/fn.spawn.html