惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

TaoSecurity Blog
TaoSecurity Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
F
Fortinet All Blogs
Cisco Talos Blog
Cisco Talos Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
S
Secure Thoughts
美团技术团队
雷峰网
雷峰网
Hugging Face - Blog
Hugging Face - Blog
博客园_首页
C
CXSECURITY Database RSS Feed - CXSecurity.com
Engineering at Meta
Engineering at Meta
人人都是产品经理
人人都是产品经理
月光博客
月光博客
T
Tor Project blog
P
Privacy & Cybersecurity Law Blog
Recorded Future
Recorded Future
I
Intezer
博客园 - 【当耐特】
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
GbyAI
GbyAI
罗磊的独立博客
V
V2EX
Google DeepMind News
Google DeepMind News
D
DataBreaches.Net
Last Week in AI
Last Week in AI
T
Tailwind CSS Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
A
About on SuperTechFans
Scott Helme
Scott Helme
Vercel News
Vercel News
Spread Privacy
Spread Privacy
T
Threat Research - Cisco Blogs
Recent Announcements
Recent Announcements
Hacker News: Ask HN
Hacker News: Ask HN
C
CERT Recently Published Vulnerability Notes
G
Google Developers Blog
B
Blog
博客园 - 叶小钗
WordPress大学
WordPress大学
博客园 - 聂微东
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Jina AI
Jina AI
IT之家
IT之家
C
Cybersecurity and Infrastructure Security Agency CISA
P
Palo Alto Networks Blog
小众软件
小众软件
博客园 - Franky
Microsoft Azure Blog
Microsoft Azure Blog
AWS News Blog
AWS News Blog

See you soon

试试将文章版本化管理吧 | See you soon 使用 Quadlet 将 Podman 中的 Postgres 当作 systemd 服务运行 | See you soon 大他者,那个无时无刻都在盯着你的东西 | See you soon Laws of Software Engineering,软件工程定律 | See you soon 浅记多因素身份认证 | See you soon Linux 内核中的度量单位 | See you soon 重置 GPG 智能密钥 | See you soon 向 NAS 引入 samba | See you soon 无法重复键入的 Fcitx5 | See you soon ZFS 降级事故 | See you soon 记被 XanMod Kernel 和 AppArmor 联合坑的一次踩坑 | See you soon agent 的 skill 与 toolcall | See you soon 记一次服务器被挂恶意挖矿二进制 | See you soon 令 acme.sh 使用 Cloudflare 的 DNS API 签发与续签证书 | See you soon 于 Tokio 中卸载 CPU Bound 任务 | See you soon 如我所见,梦破碎的时候 | See you soon 74LS 家族手册 | See you soon JDK Projects 备忘录 | See you soon 关于历史 | See you soon 用 curl 下载 OnePlus 的 ROM | See you soon 实用命令切片 | See you soon 再见,Oh My Zsh。 | See you soon 你不应该复用 strings.Builder | See you soon 博客的明日 | See you soon 被 AppArmor 击杀的 Dockge | See you soon AI 时代的自我 | See you soon 支持删除的布隆过滤器 | See you soon 基于栈的虚拟机与基于寄存器的虚拟机 | See you soon RVA23 包含了什么 | See you soon Rust 的边界检查是否已经有很大的进步 | See you soon 使用 Rust 编写操作系统:Barebones | See you soon 使用 Rust 编写操作系统:引言 | See you soon 编译器笔记:rope | See you soon 编译器笔记:CST | See you soon Paradoxical 札记 | See you soon 简单的相似去重算法(基于向量) | See you soon 更加现代的 PaperMC Minecraft 插件设计指南 | See you soon 简单的 CFG 语法分析方法 | See you soon 简单地使用 Caddy 实现 CORS 配置 | See you soon 让 OpenCV 可以被静态链接 | See you soon 图片搜索笔笺 | See you soon 电路板设计笔记-保护 | See you soon 使用 Rust 实现 SnowflakeId | See you soon 农夫乐事 FaQ 启示 | See you soon 使用 Rust 实现拓展系统札记 | See you soon 遗传学定律的代码实现 | See you soon EN:The Journey of Rust and Procps | See you soon ZH:Rust 与 Procps 之旅 | See you soon 使用 debootstrap 与 schroot 构建一个纯净环境 | See you soon GPG添加新的用户信息 | See you soon GSoC2024 笔记:使用 Rust 重新实现 procps | See you soon JWT 小册 | See you soon 使用密钥登录 SSH | See you soon Guava:Cache | See you soon 编译器笔记:增量编译 | See you soon Gradle秘境:添加一个类似modCompileOnly的依赖块 | See you soon Minecraft:原始经济系统设计简述 | See you soon TinyRemapper笔记 | See you soon MicroOS:进阶 | See you soon MicroOS:起步 | See you soon Swapfile 指北 | See you soon GPG 物理密钥的安装与密钥的迁移 | See you soon 用GPG签名告诉大家这就是我的提交 | See you soon DFS:深度优先搜索 | See you soon 文章翻译:从init.vim到init.lua | See you soon 目录遍历攻击 | See you soon 如何挂载.img文件,以及如何使用QEMU模拟arm64环境 | See you soon 在树莓派上编译 OpenCV | See you soon 关于卸载BlueStacks后遇到的问题 | See you soon
活着的 Arc | See you soon
Krysztal Huang · 2026-03-06 · via See you soon

在翻阅其他的 Rust 项目时看到了一个关于 Arc 的有趣用法,故写下本篇文章特此记录

在 Rust 中关于一个结构体,会自动派生 Drop 来实现资源释放。

在有些情况下我们可能会在一个后台任务所属的结构体中放入一个标记来检查是否已经被 drop 掉:当被 drop 掉的时候后台任务即退出。

那我们可以写出如下写法:

#[derive(Clone)]

struct Background {

dropped: Arc<AtomicBool>,

}

impl Drop for Background {

fn drop(&mut self) {

self.dropped.store(true, Ordering::Relaxed);

}

}

impl Background {

fn new() -> Background {

let dropped = Arc::new(AtomicBool::new(false));

let instance = Background {

dropped,

};

let dropped_for_task = instance.dropped.clone();

tokio::spawn(async move {

let mut tick = tokio::time::interval(Duration::from_millis(100));

tick.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);

loop {

tick.tick().await;

if dropped_for_task.load(Ordering::Relaxed) {

break;

}

// DO SOMETHING...

}

});

instance

}

}

嗯,看起来没有任何问题。

  • 后台任务启动后会判断是否已经被 drop
  • 所有从 Background 克隆的对象都能正确的共享数据

一切都那么的岁月静好。直到你的 Background 实例被 clone 后并且 drop 掉,一切都炸开花了。

为什么?

原因很简单:droppeddrop 后被立刻设置了 true,而并没有判断是不是真正的所有持有 Background 对象的结构体是不是都已经完成了使命。

考虑到这个场景很难想象,我们使用一种分发的模式来进行简单解释:

// “分发”:同一个 Background 会被 clone 给多个使用方

async fn dispatcher(background: Background) {

let a = background.clone();

let b = background.clone();

// A 先完成使命并被 drop

drop(a);

// 正如我所说,Drop 里没有判断“是不是最后一个持有者”,

// 所以 a 的 drop 会把 shared 的 dropped 直接置为 true,

// 导致后台 loop 以及 b/background 也一起被处理掉。

assert!(b.dropped.load(Ordering::Relaxed));

}

这个场景在一些情况下非常常见:

  • 在 Axum 中分发状态到端点逻辑,但 Axum 在分发状态时实际上是对数据进行 clone
  • 类似的,所有 dispatcher 模式都会受到影响

何所归?

我们还是要从引用计数上入手。

  • 当引用计数归 0 时我们可以认为所有的引用已经消失了
  • 与此同时,我们还需要避免自身持有引用导致无法判定为引用全部被 drop

因此,我们可以引入一个 alive: Arc<()> 字段来解决这个问题。

在进行改写之后,我们可以得到如下代码:

#[derive(Clone)]

struct Background {

dropped: Arc<AtomicBool>,

alive: Arc<()>,

}

impl Drop for Background {

fn drop(&mut self) {

self.dropped.store(true, Ordering::Relaxed);

}

}

impl Background {

fn new() -> Background {

let dropped = Arc::new(AtomicBool::new(false));

let alive = Arc::new(());

let instance = Background {

dropped,

alive,

};

let dropped_for_task = instance.dropped.clone();

let alive_for_task = Arc::downgrade(&instance.alive);

tokio::spawn(async move {

let mut tick = tokio::time::interval(Duration::from_millis(100));

tick.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);

loop {

tick.tick().await;

if dropped_for_task.load(Ordering::Relaxed) {

break;

}

if alive_for_task.upgrade().is_none() {

dropped_for_task.store(true, Ordering::Relaxed);

break;

}

// 再次检查 dropped:避免在真正执行工作前的竞态窗口里被 drop 后仍多跑一次。

if dropped_for_task.load(Ordering::Relaxed) {

break;

}

// DO SOMETHING...

}

});

instance

}

}

这个技巧的关键点是:把是否还有持有者的判断交给引用计数,并且让后台任务本身不参与强记数。

  • alive: Arc<()> 是否后台任务还活着的令牌。所有 Backgroundclone 都会共享它,因此 alive 的强记数就等价于还有多少个 Background 还没有被 drop
  • 后台任务不要 clone() 这个 Arc,而是用 Arc::downgrade(&alive) 拿到 Weak<()>Weak 不会增加强记数,所以不会因为后台任务本身的引用而导致引用计数永远归不了 0。
  • 当所有 Background 的克隆都被释放后,Weak::upgrade() 会返回 None,后台任务就能确定“没有任何持有者了”,于是安全退出 loop。