Async Rust中的future可以任意組合或嵌套,以實現各種控制流。假設每個Future的執行都表示為一個節點,那么可以將異步任務的異步執行組織到一個邏輯樹中,該邏輯樹在Future的輪詢、完成和取消過程中不斷轉換。
在本文中,我們將介紹Await-Tree,一個Async Rust的調試工具。它可以分析任務中的異步調用鏈和任務之間的依賴阻塞關系,以最小的運行時開銷顯著提高系統的可觀察性和可調試性。await-tree允許開發人員在運行時轉儲這個執行樹,每個Future的跨度由instrument_await注釋。
下面我們看一個基本示例:
在Cargo.toml文件中,加入以下依賴項:
[dependencies]await-tree = "0.1.2"futures = "0.3.30"tokio = {version = "1.35.1", features = ["full"]}
代碼如下:
use std::time::Duration;use await_tree::{Config, InstrumentAwait, Registry};use futures::future::{join, pending};use tokio::time::sleep;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;}#[tokio::main]async fn main() { let mut registry = Registry::new(Config::default()); let root = registry.register((), "foo"); tokio::spawn(root.instrument(foo())); sleep(Duration::from_secs(1)).await; let tree = registry.get(&()).unwrap().to_string(); println!("{tree}");}
執行cargo run,結果如下:
foo [1.002s] baz [1.002s] pending in baz 2 [1.002s] bar [1.002s] baz in bar [1.002s] pending in baz 3 [1.002s]
在代碼中,我們有一些簡單的async函數嵌套調用和使用join并發執行。與通常的代碼不同,我們在希望跟蹤的每個關鍵future后面添加.instrument_await,并為其指定名稱。此名稱可以是靜態字符串常量,也可以包含其他運行時信息。
我們再看另外一個例子:
use std::time::Duration;use await_tree::{Config, InstrumentAwait, Registry};use futures::channel::oneshot::{self, Receiver};use futures::future::{pending, select};use futures::FutureExt;use tokio::time::sleep;async fn work(rx: Receiver<()>) { let mut fut = pending().instrument_await("fut"); let _ = select( sleep(Duration::from_millis(500)) .instrument_await("sleep") .boxed(), &mut fut, ) .instrument_await("select") .await; // 等待信號繼續 rx.instrument_await("rx").await.unwrap(); fut.await}#[tokio::main]async fn main() { let mut registry = Registry::new(Config::default()); let root = registry.register((), "work"); let (tx, rx) = oneshot::channel(); tokio::spawn(root.instrument(work(rx))); sleep(Duration::from_millis(100)).await; let tree = registry.get(&()).unwrap().to_string(); println!("{tree}"); sleep(Duration::from_secs(1)).await; let tree = registry.get(&()).unwrap().to_string(); println!("{tree}"); tx.send(()).unwrap(); sleep(Duration::from_secs(1)).await; let tree = registry.get(&()).unwrap().to_string(); println!("{tree}");}
結果如下:
work [101.181ms] select [101.066ms] fut [101.044ms] sleep [101.044ms]work [1.103s] rx [601.779ms][Detached 4] fut [1.103s]work [2.105s] fut [2.105s]
這個例子展示了如何從樹中分離并重新掛載一個span。
在本文中,我們介紹了await- tree作為Async Rust中可觀察性的強大工具。await- tree是為Async Rust原生設計的回溯工具,它允許開發者實時觀察每個異步任務的執行狀態,并分析不同future或任務之間的依賴阻塞關系。
本文鏈接:http://www.tebozhan.com/showinfo-26-74193-0.htmlRust異步編程的可觀察調試工具:Await-Tree
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
上一篇: .NET中使用BootstrapBlazor組件庫Table實操篇
下一篇: 創建線程的幾種方式?你知道嗎?