Buffer in Rust with Mutex and Condvar

后端 未结 1 782
一个人的身影
一个人的身影 2021-01-24 08:30

I\'m trying to implement a buffer with a single consumer and a single producer. I have only used POSIX Semaphores, however, they\'re not available in Rust and I\'m trying to imp

相关标签:
1条回答
  • 2021-01-24 09:01

    I would encourage you to create smaller methods and reuse existing Rust types such as Option. This will allow you to simplify your code quite a bit — only one Mutex and one Condvar:

    use std::thread;
    use std::sync::{Arc, Condvar, Mutex};
    
    #[derive(Debug, Default)]
    struct Buffer {
        data: Mutex<Option<i32>>,
        data_cv: Condvar,
    }
    
    impl Buffer {
        fn insert(&self, val: i32) {
            let mut lock = self.data.lock().expect("Can't lock");
            while lock.is_some() {
                lock = self.data_cv.wait(lock).expect("Can't wait");
            }
            *lock = Some(val);
            self.data_cv.notify_one();
        }
    
        fn remove(&self) -> i32 {
            let mut lock = self.data.lock().expect("Can't lock");
            while lock.is_none() {
                lock = self.data_cv.wait(lock).expect("Can't wait");
            }
            let val = lock.take().unwrap();
            self.data_cv.notify_one();
            val
        }
    }
    
    fn producer(buffer: &Buffer) {
        for i in 0..50 {
            println!("p: {}", i);
            buffer.insert(i);
        }
    }
    
    fn consumer(buffer: &Buffer) {
        for _ in 0..50 {
            let val = buffer.remove();
            println!("c: {}", val);
        }
    }
    
    fn main() {
        let buffer = Arc::new(Buffer::default());
    
        let b = buffer.clone();
        let p = thread::spawn(move || {
            producer(&b);
        });
    
        let b = buffer.clone();
        let c = thread::spawn(move || {
            consumer(&b);
        });
    
        c.join().expect("Consumer had an error");
        p.join().expect("Producer had an error");
    }
    

    If you wanted to have a bit more performance (benchmark to see if it's worth it), you could have Condvars for the "empty" and "full" conditions separately:

    #[derive(Debug, Default)]
    struct Buffer {
        data: Mutex<Option<i32>>,
        is_empty: Condvar,
        is_full: Condvar,
    }
    
    impl Buffer {
        fn insert(&self, val: i32) {
            let mut lock = self.data.lock().expect("Can't lock");
            while lock.is_some() {
                lock = self.is_empty.wait(lock).expect("Can't wait");
            }
            *lock = Some(val);
            self.is_full.notify_one();
        }
    
        fn remove(&self) -> i32 {
            let mut lock = self.data.lock().expect("Can't lock");
            while lock.is_none() {
                lock = self.is_full.wait(lock).expect("Can't wait");
            }
            let val = lock.take().unwrap();
            self.is_empty.notify_one();
            val
        }
    }
    
    0 讨论(0)
提交回复
热议问题