tokio之如何观测Rust异步任务的调度

文章目录

  1. 1. 环境设置
  2. 2. 代码改造
  3. 3. 观测效果
    1. 3.1. task
    2. 3.2. resource

我们知道Rust的异步是以task的调度来构建的。task作为抽象在语言层面的调度单元。

那如果想要观测其的调度执行有没有办法呢?。

有的!异步runtimetokio就有个tokio-console可以实现对异步调度的观测和分析,其对了解调度的机制和性能分析都很有帮助。

环境设置

其调度的观测是需要依赖程序段添加 console-subscriber 来上报runtime的调度信息,然后由命令行程序tokio-console进行数据的统计展示。

这个功能还属于unstable,需要引入时做一些设置。

具体来说,需要在引入tokio时启用tracing, 如

1
2
3
[dependencies]
console-subscriber = "0.2.0"
tokio = { version = "1.35.1", features = ["full", "tracing"] }

异步代码main中也需要初始化console_subscriber

1
2
3
4
5
#[tokio::main]
async fn main() {
console_subscriber::init();
// ...
}

代码运行时需要加上编译参数: RUSTFLAGS="--cfg tokio_unstable" cargo run

也可以在项目根目录用.cargo/config.toml全局配置, 如

1
2
[build]
rustflags = ["--cfg", "tokio_unstable"]

这样同时运行tokio-console就能观测异步任务的调度了。

cargo install tokio-console可以安装)

代码改造

以之前《Rust并发控制之Semaphore-两线程交替打印》代码来观测为例

修改部分详见代码注释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
use std::{sync::Arc, time::Duration};
use tokio::{sync::Semaphore, task, time::sleep};

#[tokio::main]
async fn main() {
// 注意. 初始化tracing收集
console_subscriber::init();
// 线程1的令牌桶1初始一个令牌,可以先打印1
let semaphore = Arc::new(Semaphore::new(1));
let cnt = 3;
let semaphore2 = semaphore.clone();

// 线程2的令牌桶2初始没有令牌,直到1打印后增加令牌
let semaphore_wait = Arc::new(Semaphore::new(0));
let semaphore_wait2 = semaphore_wait.clone();

// 注意. 使用task::Builder来增加task名字,否则等同tokio::spawn
let t1 = task::Builder::default()
.name("t1")
.spawn(async move {
for i in 0..cnt {
let permit = semaphore.acquire().await.unwrap();
print!("1 ");
// 注意. 增加等待时间,便于观测
sleep(Duration::from_secs(i)).await;
// 消耗令牌,不放回令牌桶1
permit.forget();
// 令牌桶2增加令牌,可以打印2
semaphore_wait2.add_permits(1);
}
})
.unwrap();

let t2 = task::Builder::default()
.name("t2")
.spawn(async move {
for i in 0..cnt {
let permit = semaphore_wait.acquire().await.unwrap();
print!("2 ");
// 注意. 增加等待时间,便于观测
sleep(Duration::from_secs(i)).await;
// 消耗令牌,不放回令牌桶2
permit.forget();
// 令牌桶1增加令牌,可以打印1
semaphore2.add_permits(1);
}
})
.unwrap();

tokio::try_join!(t1, t2).unwrap();
}

观测效果

得到的观测结果如下,可以切换为task视图(按键t)和resource视图(按键r):

task

对于task能看到调度时间(Total, Busy, Sched, Idle),次数(Polls),状态(state)等。

想详细了解时间可以看看这篇博客:task-scheduled-time-in-console

task

左右按键可以选择列,上下按键可以选择行,回车会展开对应行详情, 比如task-t2

里边能看到相应waker的一些信息,也会有更细粒度的时间分布图

比较容易发现耗时不正常的task

task-detail

resource

对于resource, 能看到执行了哪些类型的异步操作

resource

详情中是对这个操作不同时间调用的详细展开。

比如t2semaphore_wait.acquire的三次调用

resource-details

本文代码详见tokio-play

想查看更多异步观测的例子建议查看下官方的例子

如有疑问,请文末留言交流或邮件:newbvirgil@gmail.com 本文链接 : https://newbmiao.github.io/2024/01/26/rust-tokio-task-tracing.html