[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 ํƒ€์ž…์ด๋‹ค.
๊ทธ๋ž˜์„œ ์ฝ๊ธฐ ๋ฝ๊ณผ ์“ฐ๊ธฐ ๋ฝ์„ ๋ถ„๋ฆฌํ•ด์„œ ๊ตฌํ˜„ํ•œ๋‹ค.

์—„์ฒญ๋‚˜๊ฒŒ ํฐ ์ฐจ์ด๊ฐ€ ๋ฐœ์ƒํ•œ ๊ฒƒ์€ ์•„๋‹ˆ๋‹ค.
๊ธฐ๋ณธ์ ์ธ ๊ทœ์น™์€ ์ด๋ ‡๋‹ค.

  1. write ๋ฝ์ด ๊ฑธ๋ ค์žˆ์„ ๋•Œ๋Š” ๋‹ค๋ฅธ ๋ชจ๋“  ์Šค๋ ˆ๋“œ๊ฐ€ ๋Œ€๊ธฐํ•œ๋‹ค.

  2. 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