You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义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);
}

问题根源

核心问题是锁的双重释放:

  1. 线程中手动调用q.release()将锁状态设为false。
  2. 当SpinGuard实例q离开作用域时,Drop trait的实现会再次将锁状态设为false。
  3. 重复释放导致锁的同步机制失效:某个线程可能还未完成计数操作,其他线程就提前获取到锁,引发竞态条件,最终造成计数丢失。

另外,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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 04:07:06