• 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

    1. fn testCoroutine4() {
    2. thread::spawn(||{
    3. let rt = tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap();
    4. let guard1 = rt.enter();
    5. println!("trace1", );
    6. rt.spawn(doSayHi());
    7. println!("trace2", );
    8. rt.spawn(doSayHi());
    9. println!("trace3", );
    10. }).join();
    11. }

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

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

    代码2:

    1. fn testCoroutine5() {
    2. thread::spawn(||{
    3. let rt = tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap();
    4. let guard1 = rt.enter();
    5. println!("trace1", );
    6. rt.spawn(doSayHi());
    7. println!("trace2", );
    8. rt.spawn(doSayHi());
    9. println!("trace3", );
    10. thread::sleep(Duration::from_secs(10));
    11. }).join();
    12. }

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

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

    代码3:

    1. fn testCoroutine6() {
    2. thread::spawn(||{
    3. let rt = tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap();
    4. let guard1 = rt.enter();
    5. println!("trace1", );
    6. let v1 = rt.spawn(doSayHi());
    7. println!("trace2", );
    8. let v2 = rt.spawn(doSayHi());
    9. println!("trace3", );
    10. rt.block_on(v1);
    11. rt.block_on(v2);
    12. }).join();
    13. }

    输出:

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

    1.没有退出的线程

    2.有切换调用栈的入口

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

    1. fn testCoroutine4() {
    2. thread::spawn(||{
    3. let rt = tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap();
    4. let guard1 = rt.enter();
    5. println!("trace1", );
    6. rt.spawn(doSayHi());
    7. println!("trace2", );
    8. rt.spawn(doSayHi());
    9. println!("trace3", );
    10. loop {
    11. rt.block_on(async {
    12. println!("sleep start", );
    13. time::sleep(Duration::from_secs(10)).await;
    14. println!("sleep end", );
    15. });
    16. }
    17. }).join();
    18. }

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

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

    总结:

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

  • 相关阅读:
    安防综合管理系统EasyCVR平台GA/T1400视图库:基于XML的消息体格式
    影响软件质量的因素简析,看第三方软件测试机构如何提升测试效果?
    《机械原理》下 学后感
    创建一个基本的网页爬虫
    Git的使用教程
    虚拟机(VM)监控工具
    澳洲谷揽GRANAR谷物分析仪维修GR-1800蛋白检测仪
    Python基础之综合练习二
    TCP协议
    Flutter最新配置本地资源文件详解,报错看这篇文章就对了!
  • 原文地址:https://blog.csdn.net/wang_sun_1983/article/details/136404278