之前關於「RcWriter」的文章中,我們將Rc智慧型指標和「Writer」一同連用,讓「Writer」的擁有權在被其他套件搶走之後還可以有辦法繼續使用同樣的「Writer」。但如果是在並發與並行的程式中,「RcWriter」就無用武之地了。



為了解釋這個問題,我們用以下這個程式來說明:

use std::io::{self, Write};

pub struct Logger<W: Write> {
    writer: W
}

impl<W: Write> Logger<W> {
    pub fn new(writer: W) -> Logger<W> {
        Logger {
            writer
        }
    }

    pub fn write_log_line(&mut self, log: &str) -> Result<(), io::Error> {
        self.writer.write(log.as_bytes())?;
        self.writer.write(b"\n")?;
        self.writer.flush()?;

        Ok(())
    }
}
mod log;

use std::rc::Rc;
use std::cell::RefCell;

use std::io::{self, Write};
use std::thread;

pub struct RcWriter<W: Write> {
    inner: Rc<RefCell<W>>
}

impl<W: Write> RcWriter<W> {
    pub fn new(writer: Rc<RefCell<W>>) -> RcWriter<W> {
        RcWriter {
            inner: writer
        }
    }
}

impl<W: Write> Write for RcWriter<W> {
    fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
        self.inner.borrow_mut().write(buf)
    }

    fn flush(&mut self) -> Result<(), io::Error> {
        self.inner.borrow_mut().flush()
    }
}

fn main() {
    let log_result: Rc<RefCell<Vec<u8>>> = Rc::new(RefCell::new(Vec::new()));

    let mut threads = Vec::with_capacity(10);

    for _ in 0..10 {
        let log_result_writer = RcWriter::new(Rc::clone(&log_result));

        let mut logger = log::Logger::new(log_result_writer);

        let thread = thread::spawn(move || {
            logger.write_log_line("New log!!").unwrap();
        });

        threads.push(thread);
    }

    for thread in threads {
        thread.join().unwrap();
    }

    println!("{:?}", log_result.borrow());
}

以上程式會編譯失敗,因為Rc智慧型指標並不是thread-safe的設計,無法跨執行緒使用。為了能夠讓RcWriter被用在不同的執行緒上,我們可以參考這篇文章的說明,改用「Arc」和「Mutex」智慧型指標來取代「Rc」和「RefCell」吧!

Synchronized Writer

「Synchronized Writer」是筆者開發的套件,將這篇文章介紹的「Rc Writer」套件,以「Arc」和「Mutex」智慧型指標來實作,使其可以跨執行緒使用。

Crates.io

https://crates.io/crates/synchronized-writer

Cargo.toml

synchronized-writer = "*"

使用方法

以上程式可以用「synchronized_writer」這個crate所提供的「SynchronizedWriter」結構體改寫如下:

extern crate synchronized_writer;

mod log;

use synchronized_writer::SynchronizedWriter;

use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let log_result: Arc<Mutex<Vec<u8>>> = Arc::new(Mutex::new(Vec::new()));

    let mut threads = Vec::with_capacity(10);

    for _ in 0..10 {
        let log_result_writer = SynchronizedWriter::new(Arc::clone(&log_result));

        let mut logger = log::Logger::new(log_result_writer);

        let thread = thread::spawn(move || {
            logger.write_log_line("New log!!").unwrap();
        });

        threads.push(thread);
    }

    for thread in threads {
        thread.join().unwrap();
    }

    println!("{:?}", *log_result.lock().unwrap());
}

如果想要取回「Writer」的擁有權,可以用「SynchronizedOptionWriter」結構體改寫成:

extern crate synchronized_writer;

use synchronized_writer::SynchronizedOptionWriter;

use std::sync::{Arc, Mutex};
use std::thread;

fn main() {
    let log_result: Arc<Mutex<Option<Vec<u8>>>> = Arc::new(Mutex::new(Some(Vec::new())));

    let mut threads = Vec::with_capacity(10);

    for _ in 0..10 {
        let log_result_writer = SynchronizedOptionWriter::new(Arc::clone(&log_result));

        let mut logger = log::Logger::new(log_result_writer);

        thread::spawn(move || {
            logger.write_log_line("New log!!").unwrap();
        });

        threads.push(thread);
    }

    for thread in threads {
        thread.join().unwrap();
    }

    println!("{:?}", log_result.lock().unwrap().take());
}