自定义SpinLock实现异常求助:多线程计数结果不符
自定义SpinLock计数异常问题排查与修复
问题背景
在Ubuntu 22.04.3LTS环境(4GB内存、AMD® Pro a4-3350b APU 64位处理器)下实现自定义SpinLock后,运行测试代码出现断言失败:预期计数为10000000,实际仅得到9999995。
错误输出
loki@loki:~/main/vs/actic/rust-nomic/spin-lock$ cargo run RUST_BACKTRACE=1 Compiling spin-lock v0.1.0 (/home/loki/main/vs/actic/rust-nomic/spin-lock) Finished dev [unoptimized + debuginfo] target(s) in 0.98s Running `target/debug/spin-lock RUST_BACKTRACE=1` Hello, world! thread 'main' panicked at 'assertion failed: `(left == right)` left: `9999995`, right: `10000000`', src/main.rs:15:5 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
代码实现
safe.rs
use core::ops::{Deref,DerefMut}; use core::sync::atomic::{AtomicBool,Ordering::{Acquire,Release}}; use core::cell::UnsafeCell; use core::hint::spin_loop; #[derive(Debug)] pub struct SpinLock<T>{ // Status true -> locked || Status false -> unlocked(ready to lock) status:AtomicBool, pub data:UnsafeCell<T> } pub struct SpinGuard<'a,T>{ lock:&'a SpinLock<T> } unsafe impl<T> Sync for SpinLock<T> where T:Send{} impl<T> SpinLock<T>{ #[inline] pub const fn new(data:T)->Self{ Self { status: AtomicBool::new(false), data: UnsafeCell::new(data) } } pub fn lock(&self)->SpinGuard<T>{ while self.status.swap(true,Acquire){ spin_loop(); } SpinGuard { lock: self } } } impl<'a,T> SpinGuard<'a,T>{ pub fn release(self){ self.lock.status.store(false, Release) } } impl<T> Deref for SpinGuard<'_,T>{ type Target = T; fn deref(&self) -> &Self::Target { unsafe{&*self.lock.data.get()} } } impl<T> DerefMut for SpinGuard<'_,T>{ fn deref_mut(&mut self) -> &mut Self::Target { unsafe{&mut *self.lock.data.get()} } } impl<T> Drop for SpinGuard<'_,T>{ fn drop(&mut self) { self.lock.status.store(false, Release) } }
main.rs
use std::thread; mod safe; fn main() { println!("Hello, world!"); let mut x=safe::SpinLock::new(0); thread::scope(|t|{ for _ in 0..10000000{ t.spawn(||{ let mut q=x.lock(); *q+=1; q.release(); }); } }); assert_eq!(x.data.get_mut(),&mut 10000000); println!("{:?}",x); }
问题根源
核心问题是锁的双重释放:
- 线程中手动调用
q.release()将锁状态设为false。 - 当
SpinGuard实例q离开作用域时,Droptrait的实现会再次将锁状态设为false。 - 重复释放导致锁的同步机制失效:某个线程可能还未完成计数操作,其他线程就提前获取到锁,引发竞态条件,最终造成计数丢失。
另外,main.rs中直接通过x.data.get_mut()读取数据也是不安全的——这绕过了SpinLock的同步机制,即使所有线程结束,也可能因内存可见性问题读取到旧值。
修复方案
方案一:移除手动释放,依赖Drop自动处理
直接删除main.rs中的q.release(),让SpinGuard的Drop实现自动负责锁的释放:
t.spawn(||{ let mut q=x.lock(); *q+=1; // 移除q.release(),由Drop自动释放锁 });
同时修改断言逻辑,通过锁安全读取数据:
let guard = x.lock(); assert_eq!(*guard, 10000000);
方案二:保留手动释放能力,避免双重释放
如果需要手动释放的功能,给SpinGuard添加一个标志位跟踪锁是否已释放,确保release和Drop仅执行一次释放:
// 修改SpinGuard结构 pub struct SpinGuard<'a,T>{ lock:&'a SpinLock<T>, released: bool, } // 修改release方法 impl<'a,T> SpinGuard<'a,T>{ pub fn release(&mut self){ if !self.released { self.lock.status.store(false, Release); self.released = true; } } } // 修改Drop实现 impl<T> Drop for SpinGuard<'_,T>{ fn drop(&mut self) { self.release(); // 复用release逻辑,避免重复代码 } } // 修改SpinLock::lock中SpinGuard的创建 impl<T> SpinLock<T>{ // ... 其他代码 ... pub fn lock(&self)->SpinGuard<T>{ while self.status.swap(true,Acquire){ spin_loop(); } SpinGuard { lock: self, released: false } } }
验证效果
修复后重新运行代码,断言会成功触发,计数将准确达到10000000。
内容的提问来源于stack exchange,提问作者Lokesh
相关产品推荐
相关产品推荐

