You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用#[tokio::main]宏创建两个运行时无法并行运行的问题

问题:Tokio两个运行时无法并行执行任务?

我正在尝试理解Tokio运行时的工作机制,特意用#[tokio::main]宏创建了两个运行时,分别执行函数a()和b()。原本以为它们会持续并行打印"im awake A"和"im awake B"(因为都调用了带循环的async_task函数),但实际程序只打印"im awake A"。既然每个运行时都有自己的线程池,为什么它们无法并行运行?

代码示例

use std::thread;
fn main() {
    a();
    b();
}

#[tokio::main]
async fn a() {
    tokio::spawn(async move { async_task("A".to_string()).await });
}

pub async fn async_task(msg: String) {
    loop {
        thread::sleep(std::time::Duration::from_millis(1000));
        println!("im awake {}", msg);
    }
}
#[tokio::main]
async fn b() {
    tokio::spawn(async move { async_task("B".to_string()).await });
}

解答

核心问题1:#[tokio::main]生成的函数会阻塞当前线程直到运行时退出

#[tokio::main]宏会把你的async fn转换成同步函数,这个函数的逻辑是:创建Tokio运行时 → 在运行时执行你的异步代码 → 阻塞当前线程,直到运行时完全停止。

所以当你在main里调用a()时,主线程会被直接卡住,永远不会执行到b()——这就是你只看到"A"输出的根本原因。

核心问题2:thread::sleep会阻塞Tokio工作线程

就算你能让两个运行时都启动,thread::sleep是同步阻塞调用,会直接占满Tokio的工作线程,导致运行时没法调度其他任务。异步代码里必须用tokio::time::sleep,它是非阻塞的,等待时会让出线程给其他任务使用。

修正方案

思路1:用同一个Tokio运行时调度两个任务(推荐)

这是Tokio的标准用法,不需要创建多个运行时:

use tokio::time;

#[tokio::main]
async fn main() {
    // 同时启动两个异步任务
    let task_a = tokio::spawn(async_task("A".to_string()));
    let task_b = tokio::spawn(async_task("B".to_string()));
    
    // 等待两个任务完成(这里是无限循环,会一直运行)
    tokio::join!(task_a, task_b);
}

pub async fn async_task(msg: String) {
    loop {
        time::sleep(time::Duration::from_millis(1000)).await;
        println!("im awake {}", msg);
    }
}

思路2:强制用两个独立运行时(需放到单独线程)

如果确实需要两个独立的Tokio运行时,得把每个#[tokio::main]修饰的函数放到单独线程执行,避免阻塞主线程:

use std::thread;
use tokio::time;

fn main() {
    // 把a放到单独线程运行
    thread::spawn(|| {
        a();
    });
    
    // 把b放到单独线程运行
    thread::spawn(|| {
        b();
    });
    
    // 主线程不能退出,否则整个程序会终止
    loop {
        thread::sleep(std::time::Duration::from_secs(1));
    }
}

#[tokio::main]
async fn a() {
    tokio::spawn(async_task("A".to_string())).await.unwrap();
}

#[tokio::main]
async fn b() {
    tokio::spawn(async_task("B".to_string())).await.unwrap();
}

pub async fn async_task(msg: String) {
    loop {
        time::sleep(time::Duration::from_millis(1000)).await;
        println!("im awake {}", msg);
    }
}

关键总结

  • #[tokio::main]生成的函数是同步阻塞的,调用后会占住当前线程直到运行时结束
  • 异步代码中禁止使用thread::sleep这类同步阻塞调用,必须用Tokio提供的异步版本
  • 除非有特殊需求,否则应该用单个Tokio运行时管理所有异步任务,这是Tokio的设计初衷

内容的提问来源于stack exchange,提问作者ezio

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 10:40:58