跳到主要内容

spawn

搜索

函数 spawn 

源代码
pub fn spawn<F>(future: F) -> JoinHandle<F::Output> 
where F: Future + Send + 'static, F::Output: Send + 'static,
展开描述

生成一个新的异步任务,并为其返回一个 JoinHandle

调用 spawn 时,所提供的 future 将立即开始在后台运行,即使你没有 await 返回的 JoinHandle

生成任务使该任务能够与其他任务并发执行。生成的任务可能在当前线程上执行,也可能被发送到另一个线程执行。具体细节取决于当前的 Runtime 配置。在正在运行的运行时中,任务将立即在后台启动。在阻塞的运行时上,用户必须推动运行时向前(例如,通过调用 Runtime::block_on)。

保证 spawn 不会同步轮询正在生成的任务。这意味着在持有锁时调用 spawn 不会与生成的任务产生死锁风险。

不能保证生成的任务一定会执行到完成。关闭运行时时,所有未完成的任务都将被 drop,无论该任务的生命周期如何。

此函数必须从 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
        });
    }
}

要并行运行多个任务并接收它们的结果,可以将 join handle 存储在向量中。

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 运行时的外部调用,则会发生 panic。

§Using !Send values from a task

提供给 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();
}

在跨 .await 调用之间持有 !Send 值将导致类似于以下的、不友好的编译错误消息:

`[... some type ...]` cannot be sent between threads safely

或:

error[E0391]: cycle detected when processing `main`