关于 tokio-async-code-review
tokio-async-code-review 是面向 Rust 代码审查者的 Tokio 异步代码检查技能。它覆盖运行时、阻塞操作、任务、同步原语和通道,并要求依据依赖与代码位置验证问题。
主要功能
- 01检查 Tokio 依赖与功能标志
- 02确认运行时模型
- 03扫描异步代码中的阻塞操作
- 04审查任务与同步原语
- 05核对通道模式和取消安全
适用场景
- 01审查 Tokio 功能标志与运行时配置
- 02查找异步函数中的阻塞调用
- 03检查任务句柄与错误处理
- 04评估锁、通道和取消行为
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ existential-birds/ beagle --skill tokio-async-code-review安装后在 Claude Code 中输入/tokio-async-code-review确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/channels.mdpinning-cancellation.mdsync-primitives.mdtask-management.md详细介绍
Tokio 异步代码审查
审查工作流
- 检查 Cargo.toml — 记录 tokio 功能标志(
full、rt-multi-thread、macros、sync等)。缺少功能会导致令人困惑的编译错误。 - 检查运行时设置 — 使用了
#[tokio::main]还是手动构建运行时?采用多线程还是当前线程? - 扫描阻塞操作 — 在异步函数中搜索
std::fs、std::net、std::thread::sleep和 CPU 密集型循环。 - 检查通道用法 — 根据通信模式匹配通道类型(mpsc、broadcast、oneshot、watch)。
- 检查同步原语 — 确认互斥锁类型正确、守卫的生命周期恰当,且不存在潜在死锁。
关卡(得出结论前的客观检查项)
按照顺序完成与审查范围相关的检查。在相关关卡通过之前,不要断言存在 Critical 或 Major 问题。
- 依赖范围 — 阅读提供
tokio的 crate 的Cargo.toml(如果继承自 workspace,也要阅读 workspace 的Cargo.toml)。通过条件: 写明tokio版本和启用的功能,或明确说明没有直接的tokio依赖,并注明其来源(workspace/path)。 - 运行时模型 — 找出运行时的构建位置(
#[tokio::main]、Runtime::builder、测试,或不拥有运行时的库)。通过条件: 用一行说明运行时类型(multi_thread/current_thread/ 仅测试 / 无),并注明定义位置。 - 阻塞操作清单 — 搜索已审查路径中的阻塞 API(
std::fs::、没有异步封装的std::net::、std::thread::sleep、async fn中的高开销 CPU 循环)。通过条件: 将每个命中项列为path:line(或工具输出摘录),或在搜索后明确写明“已审查的异步代码中未发现阻塞模式”。 - 协议 — 加载 review-verification-protocol 技能。通过条件: 在报告任何发现之前满足其通过条件(断言问题时提供文件:行号证据)。
输出格式
按以下格式报告发现:
[FILE:LINE] ISSUE_TITLE
Severity: Critical | Major | Minor | Informational
Description of the issue and why it matters.
快速参考
| 问题类型 | 参考资料 |
|---|---|
| 任务生成、JoinHandle、结构化并发 | references/task-management.md |
| Mutex、RwLock、Semaphore、Notify、Barrier | references/sync-primitives.md |
| mpsc、broadcast、oneshot、watch 通道模式 | references/channels.md |
| Pin、取消、Future 内部机制、select!、阻塞桥接 | references/pinning-cancellation.md |
审查清单
运行时配置
- Cargo.toml 中的 Tokio 功能与实际用法一致
- 运行时类型与工作负载匹配(I/O 密集型使用
multi_thread,简单场景使用current_thread) - 异步测试使用
#[tokio::test](而非手动构建运行时) - 为生产环境适当配置工作线程数量
任务管理
- 跟踪
spawn的返回值(JoinHandle),而不是静默丢弃 - 对 CPU 密集型或同步 I/O 操作使用
spawn_blocking - 任务能够响应取消(通过
CancellationToken、select!或关闭通道) - 处理
JoinError(任务 panic 或取消),而不是只使用 unwrap -
tokio::select!分支可安全取消 - 尽可能使用 trait 中原生的
async fn,而非async-traitcrate(自 Rust 1.75 起已稳定) - 检查异步上下文中的 RPIT 生命周期捕获——在 edition 2024 中,
-> impl Future现在会捕获作用域内的所有生命周期
同步原语
- 跨
.await持有锁时使用tokio::sync::Mutex;短暂的非异步代码段使用std::sync::Mutex - 不要跨越 await 点持有互斥锁守卫(有死锁风险)
- 使用
Semaphore限制并发操作(而非临时计数器) - 读操作较多时使用
RwLock(读者众多、写入较少) - 使用
Notify进行简单通知(避免通道开销) - 对运行时初始化的单例使用
std::sync::LazyLock,而非once_cell::sync::Lazy或lazy_static!(自 Rust 1.80 起稳定) - 检查
if let锁守卫模式是否受 edition 2024 临时值作用域影响——临时值会更早释放,可能改变借用的有效性
通道
- 通道类型与模式相匹配:mpsc 用于背压,broadcast 用于扇出,oneshot 用于请求-响应,watch 用于最新值
- 有界通道的容量设置恰当(太小会导致死锁,太大会占用内存)
- 处理
SendError/RecvError(表示另一端已被丢弃) - 处理 broadcast 的
Lagged错误(接收方未能跟上) - 完成后丢弃通道发送方,以向接收方发出完成信号
定时器与休眠
- 使用
tokio::time::sleep,而非std::thread::sleep - 对可能挂起的操作使用
tokio::time::timeout包装 - 正确使用
tokio::time::interval(周期性工作使用.tick().await)
严重性分级
严重
- 在异步上下文中使用阻塞式 I/O(
std::fs::read、std::net::TcpStream),且未使用spawn_blocking - 跨
.await点持有互斥锁守卫(有潜在死锁风险) - 在异步函数中使用
std::thread::sleep(阻塞运行时线程) - 需要背压的场景使用无界通道(有 OOM 风险)
主要
- 静默丢弃
JoinHandle(错误丢失、任务变成僵尸) - 未考虑
select!的取消安全性 - 根据使用场景选择了错误的互斥锁类型(std 或 tokio)
- 网络/外部操作缺少超时设置
次要
- 对极小的异步代码块使用
tokio::spawn(开销大于收益) - 通道缓冲区过大且没有合理依据
#[tokio::main]已足够时仍手动构建运行时- 争用程度高到适合使用 tokio 异步互斥锁时,仍使用
std::sync::Mutex
提示信息
- 建议使用
tokio-util工具(例如CancellationToken) - 用于服务组合的 Tower 中间件模式
- 使用
JoinSet实现结构化并发 - 从
async-traitcrate 迁移到 trait 中的原生async fn - 从
once_cell/lazy_static迁移到std::sync::LazyLock - 使用
#[expect(lint)]代替#[allow(lint)],以便在不再需要抑制时自动清理
有效模式(不要标记)
- 短临界区使用
std::sync::Mutex— tokio 文档建议在锁内没有.await时使用此方式 - 使用
tokio::spawn,但不显式等待其完成 — 对于具有适当关闭信号的后台任务,这是有效的 - 容量为 1 的无缓冲通道 — 可用于同步屏障
- 简单二进制程序中使用
#[tokio::main(flavor = "current_thread")]— 并非所有应用都需要多线程运行时 - 在
spawn前对Arc<T>调用clone()— 将其移入任务所必需,并非不必要的克隆 - 容量较大的 broadcast 通道 — 当
Lagged错误代价较高时(事件溯源),这是有效的 - trait 中不使用
async-trait的原生async fn— 自 1.75 起稳定;在dyn分发场景中,该 crate 仍然有效 -> impl Future返回类型中的+ use<'a>— 这是正确的 edition 2024 精确捕获语法,可限制生命周期捕获- 复杂异步类型上使用
#[expect(clippy::type_complexity)]— 这是#[allow]的自动清理替代方案,当不再需要抑制时会发出警告
提交发现前
完成 Gates 后,对每个报告的问题应用 review-verification-protocol 技能(证据和处理方式遵循该技能的要求)。
评论
支持 Markdown