• rust学习(tokio协程分析一)


    代码:

    1. async fn doAsyncPrint(v:u32) {
    2. println!("start doAsyncPrint,v is {},tid is {:?}",v,system::myTid());
    3. //thread::sleep(Duration::from_secs(1));
    4. time::sleep(Duration::from_secs(10)).await;
    5. println!("end,v is {},tid is {:?}",v,system::myTid());
    6. }
    7. fn testCoroutine3() {
    8. println!("testCoroutine3 start");
    9. let array = Arc::new(BlockingQueue::<u32>::new());
    10. let arrayclosure1 = array.clone();
    11. thread::spawn(move|| {
    12. let mut count:u32 = 0;
    13. loop {
    14. //println!("thread start,count is {}",count);
    15. arrayclosure1.putLast(count);
    16. count += 1;
    17. thread::sleep(Duration::from_secs(1));
    18. }
    19. });
    20. let arrayclosure2 = array.clone();
    21. thread::spawn(move ||{
    22. let rt = tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap();
    23. let guard1 = rt.enter();
    24. println!("coroutine start,tid is {:?}", system::myTid());
    25. loop {
    26. let v = arrayclosure2.takeFirst();
    27. //println!("coroutine trace1,v is {}",v);
    28. tokio::spawn(doAsyncPrint(v));
    29. }
    30. }).join();
    31. }

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

    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

    代码如下:

    1. async fn doAsyncPrint(v:u32) {
    2. println!("start doAsyncPrint,v is {},tid is {:?}",v,system::myTid());
    3. //thread::sleep(Duration::from_secs(1));
    4. time::sleep(Duration::from_secs(10)).await;
    5. println!("end,v is {},tid is {:?}",v,system::myTid());
    6. }
    7. thread::spawn(move ||{
    8. let rt = tokio::runtime::Builder::new_multi_thread().worker_threads(1).enable_all().build().unwrap();
    9. let guard1 = rt.enter();
    10. println!("coroutine start,tid is {:?}", system::myTid());
    11. loop {
    12. let v = arrayclosure2.takeFirst();
    13. //println!("coroutine trace1,v is {}",v);
    14. tokio::spawn(doAsyncPrint(v));
    15. }
    16. }).join();

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

    我们看一下运行结果:

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

    1. async fn doAsyncPrint(v:u32) {
    2. println!("start doAsyncPrint,v is {},tid is {:?}",v,system::myTid());
    3. //thread::sleep(Duration::from_secs(1));
    4. time::sleep(Duration::from_secs(10)).await;
    5. println!("end,v is {},tid is {:?}",v,system::myTid());
    6. }

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

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

  • 相关阅读:
    DDD之上下文映射图(Context Mapping)
    神奇!这款 Vue 后台框架居然不用手动配置路由
    【愚公系列】2022年09月 微信小程序-WebGL纹理材质的使用
    Linux常用命令
    Linux下的网络编程——B/S模型HTTP(四)
    Cookie简介
    缓存同步canal实现(订阅binlog)
    LVS+keepalived高可用负载均衡集群
    【Redis】Redis 安装启动使用流程
    快递排序Java
  • 原文地址:https://blog.csdn.net/wang_sun_1983/article/details/136369968