rust学习(tokio协程分析一)

代码:

rust 复制代码
async fn doAsyncPrint(v:u32) {
    println!("start doAsyncPrint,v is {},tid is {:?}",v,system::myTid());
    //thread::sleep(Duration::from_secs(1));
    time::sleep(Duration::from_secs(10)).await;
    println!("end,v is {},tid is {:?}",v,system::myTid());
}

fn testCoroutine3() {
    println!("testCoroutine3 start");
    let array = Arc::new(BlockingQueue::<u32>::new());

    let arrayclosure1 = array.clone();
    thread::spawn(move|| {
        let mut count:u32 = 0;
        loop {
            //println!("thread start,count is {}",count);
            arrayclosure1.putLast(count);
            count += 1;
            thread::sleep(Duration::from_secs(1));
        }
    });

    let arrayclosure2 = array.clone();
    thread::spawn(move ||{
        let rt = tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap();
        let guard1 = rt.enter();
        println!("coroutine start,tid is {:?}", system::myTid());
        loop {
            let v = arrayclosure2.takeFirst();
            //println!("coroutine trace1,v is {}",v);
            tokio::spawn(doAsyncPrint(v));
        }
        
    }).join();
}

代码运行时会直接卡住,这个和协程的原理有关:

1.let rt = tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap();

这里创建一个协程的runtime,注意:这里协程是跑在当前的线程中

2.let v = arrayclosure2.takeFirst();

从blocking队列中获取数据,注意这个blocking队列使用了mutex和condvar,所以当condvar卡住的时候,实际上是整个线程直接卡住,由于协程只是在线程中来回切换调用栈,所以协程中所有的等待处理实际上都是切换调用栈,但是这个condvar不是切换调用栈,所以就会卡住。

3.tokio::spawn(doAsyncPrint(v));

doAsyncPrint一直没有被调用

方案:

将new_current_thread改成new_multi_thread

代码如下:

rust 复制代码
async fn doAsyncPrint(v:u32) {
    println!("start doAsyncPrint,v is {},tid is {:?}",v,system::myTid());
    //thread::sleep(Duration::from_secs(1));
    time::sleep(Duration::from_secs(10)).await;
    println!("end,v is {},tid is {:?}",v,system::myTid());
}

thread::spawn(move ||{
        let rt = tokio::runtime::Builder::new_multi_thread().worker_threads(1).enable_all().build().unwrap();
        let guard1 = rt.enter();
        println!("coroutine start,tid is {:?}", system::myTid());
        loop {
            let v = arrayclosure2.takeFirst();
            //println!("coroutine trace1,v is {}",v);
            tokio::spawn(doAsyncPrint(v));
        }
        
    }).join();

这样实际上协程的runtime会创建线程,让协程跑在这些线程上,如上述代码,我们通过worker_threads创建了一个线程,所有的协程都会跑在这个线程上。

我们看一下运行结果:

你会发现,所有的协程都跑在了thread4上,我们看一下代码:

rust 复制代码
async fn doAsyncPrint(v:u32) {
    println!("start doAsyncPrint,v is {},tid is {:?}",v,system::myTid());
    //thread::sleep(Duration::from_secs(1));
    time::sleep(Duration::from_secs(10)).await;
    println!("end,v is {},tid is {:?}",v,system::myTid());
}

发现没有,一开始的时候连续打印了多个"start doAsyncPrint",但是没有打印"end,v is ...."这个是为啥咧?原因就在time::sleep这个函数(tokio::time::sleep),实际上,这个sleep没有真让线程sleep,而且直接切换调用栈,切换到下一个函数上,所以一开始我们可以看到连续的"start doAsyncPrint"。

协程的使用还是比较要当心的,所有操作io操作啥估计都要使用tokio的接口,否者就会导致卡住。例如上面的代码,如果改成thread::sleep的话,就会直接卡住线程,输出结果就如下:

相关推荐
带刺的坐椅12 分钟前
(对标 Spring IA 和 LangChain4j)Solon AI & MCP v3.7.0, v3.6.4, v3.5.8 发布(支持 LTS)
java·spring·ai·solon·mcp·langchain4j
林太白13 分钟前
rust18-通知管理模块
后端·rust
7澄114 分钟前
深入解析 LeetCode 1572:矩阵对角线元素的和 —— 从问题本质到高效实现
java·算法·leetcode·矩阵·intellij-idea
诗9趁年华18 分钟前
缓存三大问题深度解析:穿透、击穿与雪崩
java·spring·缓存
阳光明媚sunny19 分钟前
分糖果算法题
java·算法
whltaoin19 分钟前
【JAVA全栈项目】弧图图-智能图床SpringBoot+MySQL API接口结合Redis+Caffeine多级缓存实践解析
java·redis·spring·缓存·caffeine·多级缓存
一 乐33 分钟前
医疗管理|医院医疗管理系统|基于springboot+vue医疗管理系统设计与实现(源码+数据库+文档)
java·数据库·vue.js·spring boot·后端·医疗管理系统
xiaoxiaoxiaolll42 分钟前
Nat Commun:中国团队演示光纤阵列中的里德堡阻塞,为高保真度两比特门奠定基础
学习·量子计算
华仔啊1 小时前
SpringBoot 2.x 和 3.x 的核心区别,这些变化你必须知道
java·spring boot·后端
laocooon5238578861 小时前
大数的阶乘 C语言
java·数据结构·算法