rust学习(tokio协程分析二)

例子:

我们如果使用new_current_thread来创建tokio的协程运行runtime时,

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

发现只有调用rt.block_on(...)才能触发。这里我们分析一下为何在new_current_thread的runtime下无法运行的原因。

代码1

rust 复制代码
fn testCoroutine4() {
    thread::spawn(||{
        let rt = tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap();
        let guard1 = rt.enter();
        println!("trace1", );
        rt.spawn(doSayHi());
        println!("trace2", );
        rt.spawn(doSayHi());
        println!("trace3", );
    }).join();
}

这个无法运行的情况是由于执行完最后代码println!("trace3", );之后,线程直接就退出了,没有机会做调用栈切换。

根据代码1的情况,我们考虑是否可以保持线程不退出(如代码2),这样是否可以执行到协程(doSayHi)呢?

代码2:

rust 复制代码
fn testCoroutine5() {
    thread::spawn(||{
        let rt = tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap();
        let guard1 = rt.enter();
        println!("trace1", );
        rt.spawn(doSayHi());
        println!("trace2", );
        rt.spawn(doSayHi());
        println!("trace3", );

        thread::sleep(Duration::from_secs(10));
    }).join();
}

和遗憾,还是没有运行doSayHi,这是为什么呢?因为thread::sleep直接把线程sleep了,协程的调用栈也没有被切换。

我们来看一下可以运行的代码:

代码3:

rust 复制代码
fn testCoroutine6() {
    thread::spawn(||{
        let rt = tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap();
        let guard1 = rt.enter();
        println!("trace1", );
        let v1 = rt.spawn(doSayHi());
        println!("trace2", );
        let v2 = rt.spawn(doSayHi());
        println!("trace3", );

        rt.block_on(v1);
        rt.block_on(v2);
    }).join();
}

输出:

我们可以看到可以正常打印了。所以我们来想一下协程需要依赖哪些情况才能运行:

1.没有退出的线程

2.有切换调用栈的入口

按照上面的思路,我们是否不用block_on(v1)也能保证协程运行能?其实是可以的,参看

rust 复制代码
fn testCoroutine4() {
    thread::spawn(||{
        let rt = tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap();
        let guard1 = rt.enter();
        println!("trace1", );
        rt.spawn(doSayHi());
        println!("trace2", );
        rt.spawn(doSayHi());
        println!("trace3", );

        loop {
            rt.block_on(async {
                println!("sleep start", );
                time::sleep(Duration::from_secs(10)).await;
                println!("sleep end", );
            });
        }
    }).join();
}

哈哈,可以看到,线程最后启动了一个和之前毫无关系的协程代码,里面只是一个sleep(注意!!这个是tokio的sleep),这个sleep的作用就是满足线程有一个协程切换的入口点。我们运行一下看看结果:

正常运行。不过这个代码只是用来验证我们对协程的认识,不建议正式代码中使用。

总结:

之前用过C语言的libco,它的原理是通过注册了libc的大部分posix的io操作函数,然后通过epoll来实现异步io来实现。例如读取socket的数据,如果没有数据,由于是异步io,所以直接切换到下一个调用栈。这样就实现了简单的协程。rust这块貌似对整个协程做了统一的接口管理?在协程中的sleep等操作一定要调用tokio的接口,否则就会直接造成卡死。哈哈。不过如果是multi_thread的话,实际上也没啥关系,就是直接将协程变成了线程操作,程序应该不会出现卡死。

相关推荐
每天题库1 小时前
架子工脚手架工题库:安全操作考点与模拟题练习指南
学习·安全·考试·题库·考证
yangmu32032 小时前
Codex 提示词优化教程:用四要素把模糊需求变成可执行任务
人工智能·学习
无敌贵点大王2 小时前
RTThread学习记录13——关于要使用一个外设,RTThread与cubemx到底要怎么配合?
c语言·stm32·学习·rtthread
喜欢打篮球的普通人4 小时前
MiniMind 学习笔记(十二):Pretrain 实操——从版本梳理到 8GB 显卡上的真实训练
人工智能·笔记·学习
m4Rk_5 小时前
【论文阅读】Agent 记忆机制(87):VizoMem——把文本历史转化为可检索的视觉记忆
论文阅读·人工智能·学习·开源·github
坤坤子吖5 小时前
算法学习——高精度加减乘除
c++·笔记·学习·算法
HY小宝F6 小时前
树莓派智能摄像头系统 30 天重学计划之 Day 03 · 网络基础:建立“包“的思维 —— 学习总结
网络·学习
dadaobusi6 小时前
学习:AXI协议
学习
xxwl5856 小时前
vue3的入门学习
前端·vue.js·学习
Joe_Wang56 小时前
【从0到1学习JVM · 21】既不浪费空间又没有碎片,标记整理算法到底慢在哪
jvm·学习·算法·垃圾回收·标记整理算法