pub fn spawn<T>(future: T) -> JoinHandle<T::Output> ⓘ
Expand description
Spawns a new asynchronous task, returning a
JoinHandle
for it.
The provided future will start running in the background immediately
when spawn
is called, even if you don’t await the returned
JoinHandle
.
Spawning a task enables the task to execute concurrently to other tasks. The
spawned task may execute on the current thread, or it may be sent to a
different thread to be executed. The specifics depend on the current
Runtime
configuration.
There is no guarantee that a spawned task will execute to completion. When a runtime is shutdown, all outstanding tasks are dropped, regardless of the lifecycle of that task.
This function must be called from the context of a Tokio runtime. Tasks running on
the Tokio runtime are always inside its context, but you can also enter the context
using the Runtime::enter
method.
Examples
In this example, a server is started and spawn
is used to start a new task
that processes each received connection.
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
});
}
}
To run multiple tasks in parallel and receive their results, join handles can be stored in a vector.
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);
This example pushes the tasks to outputs
in the order they were
started in. If you do not care about the ordering of the outputs, then
you can also use a JoinSet
.
Panics
Panics if called from outside of the Tokio runtime.
Using !Send
values from a task
The task supplied to spawn
must implement Send
. However, it is
possible to use !Send
values from the task as long as they only
exist between calls to .await
.
For example, this will work:
use tokio::task;
use std::rc::Rc;
fn use_rc(rc: Rc<()>) {
// Do stuff w/ rc
}
#[tokio::main]
async fn main() {
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();
}
This will not work:
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();
}
Holding on to a !Send
value across calls to .await
will result in
an unfriendly compile error message similar to:
`[... some type ...]` cannot be sent between threads safely
or:
error[E0391]: cycle detected when processing `main`