在之前關於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
Cargo.toml
synchronized-writer = "*"
使用方法
以上程式可以用synchronized_writer
這個crate所提供的SynchronizedWriter
結構體改寫如下:
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
結構體改寫成:
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());
}