5个版本
| 0.2.1 | 2024年4月1日 |
|---|---|
| 0.2.0 | 2024年3月28日 |
| 0.1.2 | 2023年10月22日 |
| 0.1.1 | 2023年3月1日 |
| 0.1.0 | 2023年2月28日 |
#248 in 调试
9,218 每月下载量
在 2 crates 中使用
45KB
779 行
await-tree
异步Rust中的Future可以任意组合或嵌套以实现各种控制流。假设每个Future的执行表示为一个节点,那么异步任务的异步执行可以组织成一个逻辑树,该树在Future的轮询、完成和取消过程中不断转换。
await-tree允许开发者以运行时String自定义跨度,每个Future的跨度都由instrument_await标注。以下是一个基本示例,更多复杂控制流的示例可以在示例目录中找到。
async fn bar(i: i32) {
// `&'static str` span
baz(i).instrument_await("baz in bar").await
}
async fn baz(i: i32) {
// runtime `String` span is also supported
pending()
.instrument_await(format!("pending in baz {i}"))
.await
}
async fn foo() {
// spans of joined futures will be siblings in the tree
join(
bar(3).instrument_await("bar"),
baz(2).instrument_await("baz"),
)
.await;
}
let root = register("foo");
tokio::spawn(root.instrument(foo()));
sleep(Duration::from_secs(1)).await;
let tree = get_tree("foo");
// foo [1.006s]
// bar [1.006s]
// baz in bar [1.006s]
// pending in baz 3 [1.006s]
// baz [1.006s]
// pending in baz 2 [1.006s]
println!("{tree}");
与async-backtrace相比
tokio-rs/async-backtrace是一个类似的crate,也提供了转储异步任务执行树的功能。以下是await-tree和async-backtrace之间的一些区别
await-tree的优点:
-
await-tree支持使用运行时String自定义跨度,而async-backtrace仅支持函数名和行号。当我们需要使用一些动态信息,如共享资源的标识符(例如,一个锁),来注释跨度时,这很有用,以便了解不同任务之间如何发生竞争。
-
await-tree支持几乎所有类型的异步控制流,具有任意的Future拓扑,而async-backtrace无法处理其中的一些。例如,通常使用
&mut impl Future作为select的一个分支,以避免取消不安全引起的问题。为了在select完成后进一步解决这个Future,我们可以将其移动到另一个位置并在那里await它。async-backtrace由于其父级的变化而无法再次跟踪此Future。有关更多详细信息,请参阅examples/detach.rs。 -
await-tree使用基于竞技场的数据结构维护树结构,没有额外的unsafe代码。相比之下,async-backtrace手动构建它,并且上述未处理的拓扑可能存在内存不安全的风险。值得注意的是,
await-tree已经在分布式流数据库RisingWave的生产部署中长期使用。 -
await-tree将树结构与Future本身分开维护,这使得开发人员可以在几乎无争用的情况下随时转储树,无论Future是处于活动轮询还是已经挂起。相比之下,async-backtrace必须在转储树之前等待轮询完成,这可能导致长时间的延迟。
async-backtrace的优点:
async-backtrace处于Tokio组织之下。
许可协议
await-tree采用Apache License(版本2.0)进行分发。有关更多信息,请参阅LICENSE。
依赖关系
~4–11MB
~109K SLoC