速通基础Tokio(异步Rust)

rust在语言上提供了异步抽象,但异步怎么跑是要依赖具体的运行时的。也因此,一个库想开发异步逻辑的时候(如scc异步container库),并不需要依赖tokio,只要库使用者使用tokio就行了。不过,很多库要使用tokio::spawn来创建完全解耦的task,也因此可能不得不依赖tokio。

runtime

Rust Tokio,运行时以及任务相关API - 知乎

tokio的异步代码只能在tokio的运行时中进行。

runtime.enter()会生成RAII的guard变量,变量生命周期期间程序处于对应runtime中。block_on会自动调用enter,在block_on调用完成后自动退出runtime。

fn main() -> Result<()> {
    let runtime = tokio::runtime::Builder::new_multi_thread()
        .enable_all()
        .build()?;

    runtime.block_on(async{
        tokio::spawn(async {
            tokio::time::sleep(Duration::from_secs(1)).await;
            println!("Hello World");
        }).await?;
    })?;

    Ok(())
}
fn main() -> Result<()> {
    let runtime = tokio::runtime::Builder::new_multi_thread()
        .enable_all()
        .build()?;
    let _enter_guard = runtime.enter();

    let _ = tokio::spawn(async {
        tokio::time::sleep(Duration::from_secs(1)).await;
        println!("Hello World");
    });

    thread::sleep(Duration::from_secs(3));
    Ok(())
}

两种thread

core thread: 一开始就会创建默认为CPU核心数量的core线程,即wocker线程,会获取任务进行调度。任务为Future类型。当任务执行到await(相当于yield)的时候,wocker可以进行任务切换。因此,如果一个任务一直阻塞(或长时间计算)跑不到await的话,它所在的wocker就会无法继续完成其他任务

block thread: 按需创建,任务独占线程,由闭包生成。和普通的线程差不多意思。也因此可以执行阻塞任务而不影响其他任务。任务结束后默认存活十秒从而可以被复用。

普通线程生成函数thread::spawn创建的线程与堵塞线程一样都是独占的,但是spawn_blocking返回tokio::task::JoinHandle类型实现了Future,你可以在一个异步任务中等待它,而thread::spawn做不到这点。

async

async函数调用后会返回Future,从而可以await。async函数会被分配到core thread来调度。

只有async函数才可以调用另一个async函数,因此具有传染性。在异步中调用同步代码可以用block thread解决,而在同步中调用异步代码则要新建tokio运行时:

async fn foo1() -> u32 {
    10
}

fn foo() {
	//使用new_current_thread占据当前线程,不另起炉灶
    let rt = tokio::runtime::Builder::new_current_thread()
          .enable_all()
          .build().unwrap();
  
    let num = rt.block_on(foo1());  // 调用了此异步函数
    // 或者像下面这样写
    //let num = rt.block_on(async {
    //    foo1().await
    //});
    println!("{}", num);
}

await

  • async函数或代码块需要被.await才会执行。
  • tokio::spawn和tokio::task::spawn_blocking会立即开始执行,无论它们的JoinHandle是否被.await。(因此异步任务并不是完全连续的依赖树)
  • 定时器和延时需要.await来响应它们的完成。

main宏

使用#[tokio::main]即可让main函数自动进入tokio runtime:

#[tokio::main]
async fn main() {
    println!("Hello world");
}

等价于:

fn main() {
    tokio::runtime::Builder::new_multi_thread()
        .enable_all()
        .build()
        .unwrap()
        .block_on(async {
            println!("Hello world");
        })
}

使用#[tokio::test]代替#[test]即可让测试自动进入tokio runtime。

定制线程数

在builder里面可以用worker_threads来指定worker数量。对new_current_thread的builder无效。

let runtime = tokio::runtime::Builder::new_multi_thread()  
    .worker_threads(thread_num)  
    .enable_all()  
    .build().unwrap();

time

使用tokio Timer - Rust入门秘籍

spawn内的panic

spawn内的panic不会中断程序,只会打印到stderr。但可以通过分析任务结束后返回的JoinError,若错误原因是panic,就可以通过std::panic::resume_unwind让panic生效,以中断程序。

while let Some(res) = join_set.join_next().await {  
    if let Err(e) = res {  
        if e.is_panic() {  
            std::panic::resume_unwind(e.into_panic());  
        }  
    }  
}
  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值