Rust中如何跨Tokio多线程安全共享更新可变u32数值
Tokio多任务共享可变自增u32的实现方案
你之前推测更新数值需要配合await是错的,这个场景最优选择是std::sync::Arc<std::sync::atomic::AtomicU32>,全程无锁、不需要任何异步等待操作,性能拉满还完全线程安全。
- 选型逻辑:
AtomicU32是标准库提供的原子类型,基于CPU硬件级原子指令实现,fetch_add(自增)、load(读值)操作都是同步完成的,执行时间只有纳秒级,不会阻塞Tokio的异步工作线程,根本不需要await。Arc是原子引用计数指针,负责把计数器的所有权安全共享给多个spawn出来的Tokio任务,每个任务只需要clone一份Arc指针就能访问同一个底层计数器。
- 最小可运行示例:
use std::sync::{ atomic::{AtomicU32, Ordering}, Arc, }; #[tokio::main] async fn main() { // 初始化初始值为0的计数器 let counter = Arc::new(AtomicU32::new(0)); let mut task_handles = Vec::new(); // 启动10个任务操作同一个计数器 for _ in 0..10 { let counter = counter.clone(); task_handles.push(tokio::spawn(async move { // 安全自增1,返回自增前的值 let old_val = counter.fetch_add(1, Ordering::SeqCst); // 读取当前最新值 let current_val = counter.load(Ordering::SeqCst); println!("自增前值: {}, 自增后值: {}", old_val, current_val); })); } // 等待所有任务执行完成 for handle in task_handles { handle.await.unwrap(); } println!("最终计数结果: {}", counter.load(Ordering::SeqCst)); }
- 注意事项:
- 内存序没有特殊性能需求直接用
Ordering::SeqCst就行,逻辑绝对安全,不会出现跨线程值不可见的问题;如果要做极致性能优化再根据场景调整内存序,新手别上来就用Relaxed,很容易踩可见性bug。 - 绝对不要在这个场景用
tokio::sync::Mutex,这个锁是专门给「持有锁期间需要执行await异步操作」的场景设计的,用在这种纳秒级的简单操作上纯纯增加不必要的开销。 - 也没必要用
std::sync::Mutex<u32>,虽然功能上能跑,但互斥锁的开销比原子操作高几个量级,属于杀鸡用牛刀。
- 内存序没有特殊性能需求直接用
内容的提问来源于stack exchange,提问作者Test
相关产品推荐
相关产品推荐

