[Rust] lock ๋๊ธฐํ: Mutex, RwLock
๋ฌ์คํธ์์ ์ค๋ ๋๋ฅผ ์ฌ์ฉํด์ ๋์์ฑ์ ๊ตฌํํ ๋๋, ๋ฌ์คํธ์ ๊ทผ์ํ ๊ท์น์ ๋ฐ๋ผ ํญ์ ์์ ํ ๋๊ธฐํ ๊ธฐ๋ฅ์ ์ด์ฉํด์ผ ํ๋ค.
๊ทธ๋ด๋ ์ฌ์ฉํ๋๊ฒ ์ฑ๋์ด๋ Atomic, ๊ทธ๋ฆฌ๊ณ Lock์ธ๋ฐ, ์ค๋์ Lock ์ค์์๋ ์์ฃผ ์ฌ์ฉํ๋ Mutex์ RWLock์ ๋ค๋ค๋ณด๊ฒ ๋ค.
Mutex
Mutex๋ ๋ด๋ถ ๊ฐ๋ณ์ฑ ํจํด์ ๊ตฌํํ ์ค๋งํธ ํฌ์ธํฐ ํ์ ์ค ํ๋๋ค.
Rust๋ฟ๋ง ์๋๋ผ ๋ฒ์ฉ์ ์ผ๋ก ๋ค ์ฐ์ด๋ ๊ธฐ์ ์ธ๋ฐ, ๋ฐ์ด๋๋ฆฌ ์ธ๋งํฌ์ด๋ผ๊ณ ๋ ๋ถ๋ฅธ๋ค.
๋ด๋ถ์ ์ผ๋ก lock ํ๋๊ทธ๊ฐ์ ํ๋ ๋๊ณ , ์ค๋ ๋์์ lock()์ผ๋ก ์ ๊ทผํ๋ฉด ๋ค๋ฅธ ์ค๋ ๋์์์ ์ ๊ธ์ ์ฐจ๋จํ๊ณ , lock()์ด drop๋๋ฉด ์ ๊ทผ์ด ํ๋ฆฌ๋ ํํ๋ก ์๊ฒฉํ serialize๋ฅผ ๊ตฌํํ๋ค. ํ๋ฒ์ ํ๋๋ง ๋ค์ด์ค๊ฒ ํ๋ ๊ฒ์ด๋ค.
๋งค์ฐ ๋จ์ํ๊ณ ๊ฐ๋ ฅํ์ง๋ง, ์ฑ๋ฅ์ ์์ค๊ณผ ๋ฐ๋๋ฝ์ ํญ์ ๊ฒฝ๊ณํด์ผํ๋ค.
์๋์ ๊ฐ์ ํํ๋ก ์ฌ์ฉํ ์ ์๋ค.
use std::sync::{Arc, Mutex};
fn main() {
let mutex = Arc::new(Mutex::new(10));
println!("start");
{
let a = mutex.lock().unwrap(); // lock ํ๋
println!("a: {}", a);
} // a์ unlock์ ์ฌ๊ธฐ์ ๋ฐ์
println!("end");
}
Mutex ๊ฐ์ Lock ํ์
์ด๋, Atomic ํ์
๋ค์ Arc๋ก ๊ฐ์ธ์ ์ฌ์ฉํ๋๊ฒ ๊ธฐ๋ณธ์ด๋ค. ๊ทธ๋์ผ ์ค๋ ๋ ๊ฐ์ ๋ณต์ ๊ฐ ๊ฐ๋ฅํด์ง๊ธฐ ๋๋ฌธ์ด๋ค.
๊ทธ๋ฆฌ๊ณ lock ํจ์๋ฅผ ํตํด์ ์๋ณธ ๊ฐ์ ์ ๊ทผ ๊ฐ๋ฅํ MutexGuard๋ผ๋ ์ ์ฌ ํฌ์ธํฐ ๊ฐ์ฒด๋ฅผ ๋ฐํํ๋ค. ์ด๊ฑธ๋ก ๋ฝ์ ๊ฑฐ๋ ๊ฒ์ด๊ณ , ์ด MutexGuard๊ฐ drop๋๋ฉด unlock์ด ๋๋ ๊ฒ์ด๋ค.

๊ฐ์ฅ ์ฃผ์ํด์ผํ ์ผ์ด์ค์ธ ๋ฐ๋๋ฝ์ ํ๋ฒ ๋ณด๊ฒ ๋ค.
์๋ ์ฝ๋๋ ๋ฐ๋๋ฝ์ด ๋ฐ์ํ๋ ์ฝ๋๋ค.
๋ฌด์ฌ์ฝ ์งค ์๋ ์๋, ๋งค์ฐ ๊ฐ๋จํ ํํ์ ์ฝ๋์์๋
use std::sync::{Arc, Mutex};
fn main() {
let mutex = Arc::new(Mutex::new(10));
println!("start");
{
let a = mutex.lock().unwrap(); // lock ํ๋
let b = mutex.lock().unwrap(); // lock ๋๊ธฐ์ค...
println!("a: {}, b: {}", a, b);
} // a์ unlock์ ์ฌ๊ธฐ์ ๋ฐ์
println!("end");
}
๋ฐ๋๋ฝ์ด ํฐ์ง๋ค.
์ฒซ๋ฒ์งธ๋ก ํธ์ถํ lock ๊ฐ์ฒด๋ ๋ถ๋ช
ํ ์ ์ค์ฝํ๋ฅผ ๋ฒ์ด๋์ผ ํด์ ๊ฐ ๋๋ค.
๊ทผ๋ฐ ๊ทธ๋ฌ๊ธฐ๋ ์ ์ ๋๋ฒ์งธ๋ก lock์ ํธ์ถํ๋, ๋ง๋ ์๋๋ ๋ฌดํ๋ฃจํ์ ๊ฑธ๋ฆฌ๊ฒ ๋ ๊ฒ์ด๋ค.
์ต๋ํ ์ด๋ฐ ์ํฉ์ด ๋ฐ์ํ์ง ์๊ฒ ์ ์ํด์ผ ํ๋ค.
๊ทธ๋์ lock์ ๊ฑธ๋๋ ์ต๋ํ ์ด๋ฐ ์์ผ๋ก ์ค์ฝํ๋ฅผ ์๊ฒฉํ๊ฒ ์ ํํด์ผ ํ๋ค.
๊ทธ๋ผ lock์ด ๋ฐ๋ก๋ฐ๋ก ํ๋ฆฌ๋ ๋ฐ๋๋ฝ์ ์ด๋์ ๋ ํํผํ ์ ์๋ค.
๋ฌผ๋ก ์ํธ์ฐธ์กฐํ๋ฉด์ ๋๋ฝ๊ฒ ๊ผฌ์ฌ์์๋๋ ํ๋ํ๋ ๋๋ฒ๊น
ํ๋ฉด์ ํ์ด์ผํ๋ค.
RwLock
RwLock์ ๋ค์ค read ์์ ์ ์ต์ ํ๋ Lock์ด๋ค.
Mutex์ ๊ฒฝ์ฐ์๋ read&write ๊ธฐ๋ฅ์ด ๋ถ๋ฆฌ๋์ด์์ง ์๊ณ , lock์ ํ ๋ ๋ฐํ๋๋ ๊ฐ์ ์ฝ๊ธฐ์ ์ฐ๊ธฐ๋ฅผ ์ ๋ถ ํ ์ ์๋ค.
๊ทธ๋ฐ๋ฐ ์ฐ๊ธฐ์ ๋นํ๋ฉด ์ฝ๊ธฐ ์์
์ ๋ฉํฐ์ค๋ ๋ํ๊ฒฝ์์๋ ์๋นํ ์์ ์ ์ธ ๊ธฐ๋ฅ์ด๊ณ , ๋ณดํต์ ์ข๋ ๋์์ ์ผ๋ก ๋ฐ์ํด๋ ๋ฌด๋ฐฉํ ํธ์ด๋ค. ํ์ง๋ง Mutex๋ ๊ทธ๋ฐ๊ฒ์ ๊ตฌ๋ถํ์ง ๋ชปํ๊ธฐ ๋๋ฌธ์.. ์ฝ๊ธฐ๋ง ํ๋๋ผ๋ ์ฃผ๊ตฌ์ฅ์ฐฝ lock์ ๊ฑธ์ด์ผ ํ๋ค๋๊ฒ ์ข ๋จ์ ์ด ๋ ์ ์๋ค.
RwLock์ Mutex์ ๊ทธ๋ฌํ ๋จ์ ์ ๋ณด์ํ๋ Lock ํ์
์ด๋ค.
๊ทธ๋์ ์ฝ๊ธฐ ๋ฝ๊ณผ ์ฐ๊ธฐ ๋ฝ์ ๋ถ๋ฆฌํด์ ๊ตฌํํ๋ค.
์์ฒญ๋๊ฒ ํฐ ์ฐจ์ด๊ฐ ๋ฐ์ํ ๊ฒ์ ์๋๋ค.
๊ธฐ๋ณธ์ ์ธ ๊ท์น์ ์ด๋ ๋ค.
-
write ๋ฝ์ด ๊ฑธ๋ ค์์ ๋๋ ๋ค๋ฅธ ๋ชจ๋ ์ค๋ ๋๊ฐ ๋๊ธฐํ๋ค.
-
read ๋ฝ์ด ๊ฑธ๋ ค์์ ๋๋ ๋ชจ๋ read๋ฅผ ๋์์ ํ์ฉํ๋ค. ๋จ, write๋ ๋๊ธฐ์ํจ๋ค.
๊ทธ๋์ ์๋์ ๊ฐ์ด ๋์์ read๋ฅผ ์๋ํ๋๋ผ๋, ๋ฐ๋๋ฝ์ด ๋ฐ์ํ์ง ์๋๋ค.
use std::sync::{Arc, RwLock};
fn main() {
let mutex = Arc::new(RwLock::new(10));
println!("start");
{
let a = mutex.read().unwrap();
let b = mutex.read().unwrap();
println!("a: {}, b: {}", a, b);
}
println!("end");
}

ํ์ง๋ง ์ด๋ ๊ฒ write์ read๊ฐ ํผ์ฌ๋ ๋๋ Mutex์ ๋ง์ฐฌ๊ฐ์ง๋ก ์ผ๋ง๋ ์ง ๋ฐ๋๋ฝ์ด ๋ฐ์ํ ์ ์๋ค.
use std::{
borrow::BorrowMut,
sync::{Arc, RwLock},
};
fn main() {
let mutex = Arc::new(RwLock::new(10));
println!("start");
{
let mut w: std::sync::RwLockWriteGuard<i32> = mutex.write().unwrap(); // write lock ํ๋
w.borrow_mut().clone_from(&10);
let a = mutex.read().unwrap(); // read ๋๊ธฐ
let b = mutex.read().unwrap();
mutex.write().unwrap().clone_from(&20);
println!("a: {}, b: {}", a, b);
}
println!("end");
}

์๋ฌดํผ ๊ทธ๋์ ์ฐ๊ธฐ๊ฐ ์ ๊ณ ์ฝ๊ธฐ๊ฐ ๊ต์ฅํ ๋ง์ ๋๋ RwLock์ด ๋งค์ฐ ์ ํฉํ๋ค๊ณ ํ ์ ์๋ค.
๊ทธ ๋ฐ๋๋ก ์ฐ์๊ฐ ๋ง์ ๋๋ mpsc channel ๊ฐ์๊ฒ ์ข๊ฒ ๊ณ ...
primitive๋ง ์จ๋ ๋๋ ๊ฒฝ์ฐ์๋ Atomic์ด ๊ฐ๋ณ๊ณ ๋น ๋ฅด๋ค.
Mutex๋ ๊ทธ ์ฌ์ด์ ์ ๋งคํ ์ง์ ์ผ ๋ ์ฌ์ฉํ๋ฉด ๋์ง ์๋ ์ถ๋ค.
์ฐธ์กฐ
https://onesignal.com/blog/thread-safety-rust/
https://doc.rust-lang.org/std/sync/struct.RwLock.html
https://stackoverflow.com/questions/50704279/when-or-why-should-i-use-a-mutex-over-an-rwlock