如何实现自旋锁

Lok*_*esh -4 concurrency spinlock compare-and-swap rust

我试图实现我自己的自定义 SpinLock,但 SpinLock 似乎行为不当。

\n

我有两个文件,main.rs并且safe.rs.

\n

测试在 Ubuntu 22.04.3LTS 中进行,系统规格为 4GB RAM、64 位处理器、AMD\xc2\xae Pro a4-3350b APU 和 Radeon r4 显卡。

\n

这是错误消息:

\n
loki@loki:~/main/vs/actic/rust-nomic/spin-lock$ cargo run RUST_BACKTRACE=1\n   Compiling spin-lock v0.1.0 (/home/loki/main/vs/actic/rust-nomic/spin-lock)\n    Finished dev [unoptimized + debuginfo] target(s) in 0.98s\n     Running `target/debug/spin-lock RUST_BACKTRACE=1`\nHello, world!\nthread 'main' panicked at 'assertion failed: `(left == right)`\n  left: `9999995`,\n right: `10000000`', src/main.rs:15:5\nnote: run with `RUST_BACKTRACE=1` environment variable to display a backtrace\n
Run Code Online (Sandbox Code Playgroud)\n

safe.rs:

\n
use core::ops::{Deref,DerefMut};\nuse core::sync::atomic::{AtomicBool,Ordering::{Acquire,Release}};\nuse core::cell::UnsafeCell;\nuse core::hint::spin_loop;\n\n#[derive(Debug)]\npub struct SpinLock<T>{\n    // Status true -> its locked ||  Status false -> its un_locked(ready to lock)\n    status:AtomicBool,\n    pub data:UnsafeCell<T>\n}\n\npub struct SpinGuard<'a,T>{\n    lock:&'a SpinLock<T>\n}\nunsafe impl<T> Sync for SpinLock<T> where T:Send{}\n\nimpl<T> SpinLock<T>{\n    #[inline]\n    pub const fn new(data:T)->Self{\n        Self { status: AtomicBool::new(false), data: UnsafeCell::new(data) }\n    }\n    pub fn lock(&self)->SpinGuard<T>{\n        while self.status.swap(true,Acquire){\n            spin_loop();\n        }\n        SpinGuard { lock: self }\n    }\n}\n\n\nimpl<'a,T> SpinGuard<'a,T>{\n    pub fn release(self){\n        self.lock.status.store(false, Release)\n    }\n}\n\nimpl<T> Deref for SpinGuard<'_,T>{\n    type Target = T;\n    fn deref(&self) -> &Self::Target {\n        unsafe{&*self.lock.data.get()}\n    }\n}\n\nimpl<T> DerefMut for SpinGuard<'_,T>{\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        unsafe{&mut *self.lock.data.get()}\n    }\n}\n\nimpl<T> Drop for SpinGuard<'_,T>{\n    fn drop(&mut self) {\n        self.lock.status.store(false, Release)\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

main.rs:

\n
use std::thread;\nmod safe;\nfn main() {\n    println!("Hello, world!");\n    let mut  x=safe::SpinLock::new(0);\n    thread::scope(|t|{\n        for _  in 0..10000000{\n            t.spawn(||{\n                let mut q=x.lock();\n                *q+=1;\n                q.release();\n            });\n        }\n    });\n    assert_eq!(x.data.get_mut(),&mut 10000000);\n    println!("{:?}",x);\n}\n
Run Code Online (Sandbox Code Playgroud)\n

Fin*_*nis 6

通过添加几个sleeps,您可以使错误更加可重现:

use std::thread;

fn main() {
    println!("Hello, world!");
    let mut x = safe::SpinLock::new(0);
    thread::scope(|t| {
        for _ in 0..100 {
            t.spawn(|| {
                let mut q = x.lock();
                let q_prev = *q;
                std::thread::sleep(std::time::Duration::from_millis(1));
                *q = q_prev + 1;
                q.release();
            });
        }
    });
    assert_eq!(x.data.get_mut(), &mut 100);
    println!("{:?}", x.data.get_mut());
}

mod safe {
    use core::cell::UnsafeCell;
    use core::hint::spin_loop;
    use core::ops::{Deref, DerefMut};
    use core::sync::atomic::{
        AtomicBool,
        Ordering::{Acquire, Release},
    };

    #[derive(Debug)]
    pub struct SpinLock<T> {
        // Status true -> its locked ||  Status false -> its un_locked(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);
            std::thread::sleep(std::time::Duration::from_millis(1));
        }
    }

    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)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)
Hello, world!
thread 'main' panicked at src\main.rs:17:5:
assertion `left == right` failed
  left: 29
 right: 100
Run Code Online (Sandbox Code Playgroud)

原因是你解锁了两次。有点隐蔽,不过.release()功能其实是直接解锁一次,在drop()功能里解锁一次。

只需删除函数中的第一个解锁即可.release(),然后就可以了:)

作为另一个挑剔,我会将Acquirein替换swapAcqRel. 但我不能 100% 确定它会产生影响。

Hello, world!
thread 'main' panicked at src\main.rs:17:5:
assertion `left == right` failed
  left: 29
 right: 100
Run Code Online (Sandbox Code Playgroud)
Hello, world!
100
Run Code Online (Sandbox Code Playgroud)