TL;DR

  • Arc์˜ atomic ์ฐธ์กฐ ์ˆ˜๋ฅผ ์ด์šฉํ•œ thread ๊ฐ„ ๊ณต์œ  ์†Œ์œ ๊ถŒ
  • MutexGuard์˜ ์ˆ˜๋ช…์œผ๋กœ ์ œํ•œํ•˜๋Š” ๋‚ด๋ถ€ ๊ฐ’์˜ ๋ฐฐํƒ€์  ์ ‘๊ทผ
  • Arc<Mutex<T>>์˜ thread๋ณ„ stack๊ณผ ๊ณต์œ  heap ํ• ๋‹น

AI-assisted


1. Thread๋ณ„ stack๊ณผ ๊ณต์œ  heap

Rc์™€ RefCell์„ ์กฐํ•ฉํ•˜๋ฉด ๊ฐ™์€ thread์˜ ์—ฌ๋Ÿฌ ์†Œ์œ ์ž๊ฐ€ ๊ฐ™์€ ๊ฐ’์„ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋‹ค. ์—ฌ๋Ÿฌ thread์—์„œ๋„ ์ด ๊ด€๊ณ„๋ฅผ ๋งŒ๋“ค๋ ค๋ฉด ๊ณต์œ  ์†Œ์œ ๊ถŒ๊ณผ ๊ฐ’์˜ ๋ณ€๊ฒฝ์„ ๊ฐ๊ฐ ๋™๊ธฐํ™”ํ•ด์•ผ ํ•œ๋‹ค.

๊ฐ OS thread๋Š” ์ž์‹ ์˜ stack๊ณผ ์ง€์—ญ ์ƒํƒœ๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค. ๊ฐ™์€ ํ”„๋กœ์„ธ์Šค์˜ heap ํ• ๋‹น์€ thread ์‚ฌ์ด์—์„œ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ์„œ๋กœ ๋‹ค๋ฅธ stack์˜ ํฌ์ธํ„ฐ๊ฐ€ ๊ฐ™์€ ๊ฐ’์„ ๊ฐ€๋ฆฌํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.

  • ๊ณต์œ  ์†Œ์œ ๊ถŒ: ๊ฐ thread๊ฐ€ ํ•„์š”๋กœ ํ•˜๋Š” ๋™์•ˆ ๊ฐ™์€ ๊ฐ’์˜ ์ˆ˜๋ช…์„ ์œ ์ง€ํ•œ๋‹ค. Rc๋Š” cloneยทdrop ๋•Œ ์ฐธ์กฐ ์ˆ˜๋ฅผ non-atomic ์—ฐ์‚ฐ์œผ๋กœ ๋ฐ”๊พธ๋ฏ€๋กœ thread ์‚ฌ์ด์— ๊ณต์œ ํ•  ์ˆ˜ ์—†๋‹ค.
  • ๊ณต์œ  ๊ฐ’์˜ ๋ณ€๊ฒฝ: ์—ฌ๋Ÿฌ thread๊ฐ€ ๊ฐ™์€ ๊ฐ’์„ ์ˆ˜์ •ํ•  ๋•Œ ์ ‘๊ทผ์„ ๋™๊ธฐํ™”ํ•œ๋‹ค. RefCell์˜ borrow ๊ฒ€์‚ฌ๋Š” thread ๊ฐ„ ๋™๊ธฐํ™”๋ฅผ ์ œ๊ณตํ•˜์ง€ ์•Š๋Š”๋‹ค.

Arc๋Š” ์ฒซ ์š”๊ตฌ๋ฅผ, Mutex๋Š” ๋‘ ๋ฒˆ์งธ ์š”๊ตฌ๋ฅผ ๋งก๋Š”๋‹ค. ๋‘ ์š”๊ตฌ๊ฐ€ ํ•จ๊ป˜ ์žˆ์œผ๋ฉด Arc<Mutex<T>>๋ฅผ ์“ด๋‹ค.

๋„์‹: ๋ฉ”๋ชจ๋ฆฌ ๊ด€๊ณ„์˜ ๊ฐœ๋… ๋ชจํ˜•

thread์˜ ์ง€์—ญ ๊ฐ’์€ stack์— ํ‘œ์‹œํ•œ๋‹ค. ์‹ค์ œ ABIยท์ตœ์ ํ™”ยทOS๋ณ„ lock ๋ถ€๊ฐ€ ์ž์›์€ ์ƒ๋žตํ•˜๋ฉฐ ์†Œ์œ ยท๋นŒ๋ฆผยท๊ฐ’์˜ ํฌํ•จ ๊ด€๊ณ„๋ฅผ ๋‚˜ํƒ€๋‚ธ๋‹ค.


2. Arc: A thread-safe reference-counting pointer

A thread-safe reference-counting pointer. (Arc)

Arc<T>๋Š” heap์˜ ๊ฐ™์€ T๋ฅผ ์—ฌ๋Ÿฌ ์†Œ์œ ์ž๊ฐ€ ํ•จ๊ป˜ ์†Œ์œ ํ•˜๋Š” ํฌ์ธํ„ฐ๋‹ค. Arc::clone์€ T๋ฅผ ๋ณต์ œํ•˜์ง€ ์•Š๊ณ  ๊ฐ™์€ ํ• ๋‹น์„ ๊ฐ€๋ฆฌํ‚ค๋Š” Arc๋ฅผ ํ•˜๋‚˜ ๋” ๋งŒ๋“ ๋‹ค. ์ฐธ์กฐ ์ˆ˜์˜ ์ฆ๊ฐ์„ atomic ์—ฐ์‚ฐ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜๋ฏ€๋กœ ์—ฌ๋Ÿฌ thread์˜ cloneยทdrop์ด ๊ฒน์ณ๋„ ์ฐธ์กฐ ์ˆ˜๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ๊ด€๋ฆฌํ•œ๋‹ค.

๋‹ค์Œ ์˜ˆ์ œ์—์„œ๋Š” main๊ณผ worker๊ฐ€ ๊ฐ™์€ ์ˆซ์ž 10์„ ์ฝ๋Š”๋‹ค.

use std::sync::Arc;
use std::thread;
 
fn main() {
    let number = Arc::new(10);
    let worker_number = Arc::clone(&number);
 
    let worker = thread::spawn(move || {
        assert_eq!(*worker_number, 10);
    });
    // ๋„์‹ ์‹œ์  โ‘ : spawn ๋ฐ˜ํ™˜ ๋’ค worker_number๊ฐ€ ์•„์ง ์‚ด์•„ ์žˆ๋Š” ๊ฐ€๋Šฅํ•œ ์‹œ์ 
 
    assert_eq!(*number, 10);
    worker.join().unwrap();
    assert_eq!(Arc::strong_count(&number), 1);
}

์‹œ์  โ‘ ์€ spawn์ด ๋ฐ˜ํ™˜๋œ ๋’ค worker๊ฐ€ ์•„์ง Arc๋ฅผ ๋ณด์œ ํ•œ ๊ฐ€๋Šฅํ•œ ์‹คํ–‰ ์ƒํƒœ๋‹ค. worker๊ฐ€ ๋จผ์ € ๋๋‚  ์ˆ˜๋„ ์žˆ์œผ๋ฏ€๋กœ ์ฐธ์กฐ ์ˆ˜๊ฐ€ 1์ด๋ผ๋Š” ๊ฒ€์‚ฌ๋Š” join() ๋’ค์— ๋‘”๋‹ค.

flowchart TB
    subgraph A_MAIN["Main thread์˜ Stack"]
        A_NUMBER["number: Arc&lt;i32&gt;"]
        A_JOIN["worker: JoinHandle / ์ข…๋ฃŒ ๋Œ€๊ธฐ์šฉ"]
    end
    subgraph A_WORKER["Worker thread์˜ Stack"]
        A_WORKER_NUMBER["worker_number: Arc&lt;i32&gt;"]
    end
    subgraph A_HEAP["๊ณต์œ  Heap"]
        subgraph A_ALLOCATION["Arc ํ• ๋‹น ํ•˜๋‚˜"]
            A_COUNT["atomic strong count: 2"]
            A_VALUE["๋‚ด๋ถ€ i32 ๊ฐ’: 10"]
        end
    end
    A_NUMBER -->|๊ณต์œ  ์†Œ์œ | A_ALLOCATION
    A_WORKER_NUMBER -->|๊ณต์œ  ์†Œ์œ | A_ALLOCATION
  • move: worker_number์˜ ์†Œ์œ ๊ถŒ์„ worker์˜ closure๋กœ ๋„˜๊ธด๋‹ค. main์—๋Š” number์™€ ์ข…๋ฃŒ ๋Œ€๊ธฐ์šฉ worker๊ฐ€ ๋‚จ๋Š”๋‹ค.
  • ๊ฐ™์€ ์ˆซ์ž: ๋‘ Arc๊ฐ€ ๊ฐ™์€ ํ• ๋‹น์˜ 10์„ ์ฝ๋Š”๋‹ค. Arc::clone์€ ๋ณ„๋„ ์ˆซ์ž๋ฅผ ๋งŒ๋“ค์ง€ ์•Š๋Š”๋‹ค.
  • ์ข…๋ฃŒ ๋’ค: worker์˜ Arc๊ฐ€ drop๋˜๋ฉด number๋งŒ ๋‚จ์•„ strong count๋Š” 1์ด๋‹ค. ๋งˆ์ง€๋ง‰ Arc๋„ drop๋˜๋ฉด ๋‚ด๋ถ€ T๊ฐ€ drop๋œ๋‹ค.

์ฃผ์š” API๋Š” Rc์˜ ์ฃผ์š” method์™€ ๋น„์Šทํ•˜๋‹ค. get_mut๊ณผ make_mut๋„ ๊ฐ™์€ ์†Œ์œ ๊ถŒ ์›๋ฆฌ๋กœ ๋™์ž‘ํ•œ๋‹ค. ์—ฌ๋Ÿฌ ์†Œ์œ ์ž๊ฐ€ ์žˆ์„ ๋•Œ make_mut์œผ๋กœ ๊ฐ’์„ ๋ถ„๋ฆฌํ•˜๋ฉด ๋‹ค๋ฅธ Arc๋Š” ๊ธฐ์กด ๊ฐ’์„ ๊ณ„์† ์†Œ์œ ํ•œ๋‹ค.

Arc๊ฐ€ ๋™๊ธฐํ™”ํ•˜๋Š” ๋Œ€์ƒ์€ ์ž์‹ ์˜ ์ฐธ์กฐ ์ˆ˜๋‹ค. ๋‚ด๋ถ€ T์˜ ๋ณ€๊ฒฝ์—๋Š” T๊ฐ€ ์ œ๊ณตํ•˜๋Š” ๋™๊ธฐํ™” API๊ฐ€ ํ•„์š”ํ•˜๋ฉฐ ์ผ๋ฐ˜์ ์ธ thread ๊ฐ„ Arc ๊ณต์œ  ์กฐ๊ฑด์€ T: Send + Sync๋‹ค.


3. Mutex: A mutual exclusion primitive

main๊ณผ worker๊ฐ€ ๊ฐ™์€ ์ˆซ์ž๋ฅผ ๊ฐ๊ฐ 1์”ฉ ๋Š˜๋ฆฌ๋ ค๋ฉด ๊ฐ’์„ ์ฝ๊ณ  ๋”ํ•˜๊ณ  ์“ฐ๋Š” ๋™์•ˆ ๋‹ค๋ฅธ thread์˜ ์ˆ˜์ •์„ ๋ง‰์•„์•ผ ํ•œ๋‹ค. ๋‘ thread๊ฐ€ ๋ชจ๋‘ 0์„ ์ฝ๊ณ  1์„ ์“ฐ๋ฉด ์ฆ๊ฐ€ ํ•˜๋‚˜๊ฐ€ ๋ˆ„๋ฝ๋œ๋‹ค. Mutex<T>๋Š” lock์„ ์–ป์€ thread ํ•˜๋‚˜๊ฐ€ ๋‚ด๋ถ€ T์— ์ ‘๊ทผํ•˜๋„๋ก ์ œํ•œํ•œ๋‹ค.

A mutual exclusion primitive useful for protecting shared data (Mutex)

3.1 lock๊ณผ guard์˜ ๊ธฐ๋ณธ ๋™์ž‘

lock()์ด ์„ฑ๊ณตํ•˜๋ฉด Mutex๋ฅผ ๋นŒ๋ฆฌ๋Š” MutexGuard<T>๋ฅผ ์–ป๋Š”๋‹ค. guard๋กœ ๋‚ด๋ถ€ ๊ฐ’์„ ์ฝ๊ฑฐ๋‚˜ ์ˆ˜์ •ํ•˜๊ณ  guard๊ฐ€ drop๋  ๋•Œ unlockํ•œ๋‹ค.

When this structure is dropped (falls out of scope), the lock will be unlocked.

์ ‘๊ทผ๋‹ค๋ฅธ thread๊ฐ€ lock์„ ๋ณด์œ ํ•œ ๊ฒฝ์šฐ๋ฐ˜ํ™˜๊ณผ ํ•ด์ œ
lock()ํš๋“ํ•  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐ์„ฑ๊ณตํ•˜๋ฉด guard, guard์˜ drop์œผ๋กœ unlock
try_lock()๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๊ณ  WouldBlock ์˜ค๋ฅ˜ ๋ฐ˜ํ™˜ํš๋“์— ์„ฑ๊ณตํ•˜๋ฉด ๊ฐ™์€ guard

ํ‘œ์ค€ MutexGuard๋Š” Send๊ฐ€ ์•„๋‹ˆ๋ฏ€๋กœ lock์„ ์–ป์€ thread์—์„œ ํ•ด์ œํ•œ๋‹ค. guard์˜ ๋งˆ์ง€๋ง‰ ์‚ฌ์šฉ์ด ๊ณง drop์€ ์•„๋‹ˆ์–ด์„œ ๋ธ”๋ก์ด๋‚˜ drop(guard)๋กœ lock ์œ ์ง€ ๋ฒ”์œ„๋ฅผ ์ •ํ•œ๋‹ค.

Mutex๋Š” ๊ณต์œ  ์†Œ์œ ๊ถŒ์„ ์ œ๊ณตํ•˜์ง€ ์•Š์œผ๋ฉฐ T๋ฅผ ์ž์‹ ์˜ ํ•„๋“œ์— ๋‹ด๋Š”๋‹ค. Mutex<i32>์˜ ์ˆซ์ž๋Š” Mutex ์•ˆ์— ๋“ค์–ด๊ฐ€๊ณ  Mutex๋ฅผ ์ง€์—ญ ๊ฐ’์œผ๋กœ ๋‘๋ฉด ๊ทธ ์ˆซ์ž๋ฅผ ์œ„ํ•œ ๋ณ„๋„ heap ํ• ๋‹น์€ ํ•„์š”ํ•˜์ง€ ์•Š๋‹ค. Arc ์•ˆ์— ๋‹ด์œผ๋ฉด Mutex์™€ ์ˆซ์ž๊ฐ€ ํ•จ๊ป˜ Arc์˜ heap ํ• ๋‹น ์•ˆ์— ๋†“์ธ๋‹ค.

3.2 ๋‘ thread์˜ ๊ณต์œ  ๊ฐ’ ์ˆ˜์ •

main๊ณผ worker๊ฐ€ ๊ฐ™์€ counter๋ฅผ ๊ณต์œ ํ•˜๋ฉฐ ๊ฐ๊ฐ 1์„ ๋”ํ•˜๋Š” ์˜ˆ์ œ๋‹ค.

use std::sync::Mutex;
use std::thread;
 
fn main() {
    let counter = Mutex::new(0);
 
    thread::scope(|scope| {
	    // main thread
	    
        scope.spawn(|| {
	        // ์ด ๋ณธ๋ฌธ๋งŒ ์ƒˆ worker thread์—์„œ ์‹คํ–‰
            let mut worker_guard = counter.lock().unwrap();
            *worker_guard += 1;
        }); // worker ์•ˆ์—์„œ guard๊ฐ€ drop๋˜๋ฉฐ unlock
 
        {
			// main thread
            let mut main_guard = counter.lock().unwrap();
            *main_guard += 1;
        } // main ์•ˆ์—์„œ guard๊ฐ€ drop๋˜๋ฉฐ unlock
    }); // worker๊ฐ€ ๋๋‚  ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆผ
 
    assert_eq!(*counter.lock().unwrap(), 2);
}

counter ์ž์ฒด๋ฅผ move๋กœ worker์— ๋„˜๊ธฐ๋ฉด Mutex์˜ ์†Œ์œ ๊ถŒ๋„ worker๋กœ ์ด๋™ํ•˜๋ฏ€๋กœ main์€ ๊ฐ™์€ Mutex์— ์ ‘๊ทผํ•  ์ˆ˜ ์—†๋‹ค. worker๊ฐ€ Mutex๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ์ž‘์„ฑํ•˜๋ฉด main์ด join()์œผ๋กœ ์†Œ์œ ๊ถŒ์„ ๋Œ๋ ค๋ฐ›์•„ ๋‹ค์‹œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ๊ฒฝ์šฐ์—๋Š” worker์˜ ์ž‘์—…์ด ๋๋‚œ ๋’ค main์ด ๊ฐ™์€ Mutex๋ฅผ ์ด์–ด์„œ ์‚ฌ์šฉํ•˜๋Š” ์ˆœ์ฐจ์ ์ธ ๊ตฌ์กฐ๊ฐ€ ๋œ๋‹ค.

๋‘ thread๊ฐ€ ๊ฐ™์€ Mutex๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ด ์˜ˆ์ œ์—์„œ๋Š” thread::scope๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

  • main์€ counter์˜ ์†Œ์œ ๊ถŒ์„ ์œ ์ง€ํ•œ ์ฑ„ worker์— &Mutex<i32>๋ฅผ ๋นŒ๋ ค์ค€๋‹ค.
  • thread::scope๋Š” ๋ฐ˜ํ™˜๋˜๊ธฐ ์ „์— worker๋ฅผ ์ž๋™์œผ๋กœ joinํ•œ๋‹ค. worker๋Š” counter๊ฐ€ drop๋˜๊ธฐ ์ „์— ์ž‘์—…์„ ๋งˆ์นœ๋‹ค.
  • ๊ฐ thread๋Š” ์ž๊ธฐ ์•ˆ์—์„œ lock()์œผ๋กœ guard๋ฅผ ์–ป์–ด ๊ฐ’์„ ์ˆ˜์ •ํ•œ๋‹ค. guard๊ฐ€ drop๋  ๋•Œ ์ž ๊ธˆ์ด ํ’€๋ฆฐ๋‹ค.

3.3 lock์˜ ๋ฐ˜ํ™˜๊ฐ’๊ณผ poisoning

์˜ˆ์ œ์˜ counter.lock().unwrap()์—์„œ lock()์€ Result๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค. ์ด์ „ ์‚ฌ์šฉ ์ค‘ ๋ฐœ์ƒํ•œ panic ๋•Œ๋ฌธ์— ๋‚ด๋ถ€ ๊ฐ’์˜ ์ผ๊ด€์„ฑ์„ ํ™•์ธํ•ด์•ผ ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

  • ์ •์ƒ ์‚ฌ์šฉ: Ok(guard)๋กœ ๊ฐ’์„ ์‚ฌ์šฉํ•˜๊ณ  guard๊ฐ€ drop๋˜๋ฉด ์ž ๊ธˆ์ด ํ’€๋ฆฐ๋‹ค.
  • panic์œผ๋กœ ์ค‘๋‹จ: guard๋ฅผ ๋ณด์œ ํ•œ ์ฑ„ panic์œผ๋กœ scope๋ฅผ ๋น ์ ธ๋‚˜๊ฐ€๋ฉด guard๋ฅผ ์ •๋ฆฌํ•˜๋ฉด์„œ ์ž ๊ธˆ์„ ํ’€๊ณ  poisoned ์ƒํƒœ๋ฅผ ๊ธฐ๋กํ•œ๋‹ค. ๋ฐ์ดํ„ฐ ์ˆ˜์ •์ด ์ค‘๊ฐ„์— ๋Š๊ฒผ์„ ์ˆ˜ ์žˆ๋‹ค๋Š” ํ‘œ์‹œ๋‹ค.
  • ์ดํ›„ ์ ‘๊ทผ: poisoned ์ƒํƒœ์—์„œ๋Š” ๋‹ค์Œ lock()์ด ์ž ๊ธˆ์„ ์–ป๋”๋ผ๋„ Err(PoisonError)๋ฅผ ๋ฐ˜ํ™˜ํ•ด ๋ฐ์ดํ„ฐ ํ™•์ธ์ด ํ•„์š”ํ•จ์„ ์•Œ๋ฆฐ๋‹ค.

์˜ˆ์ œ์˜ .unwrap()์€ ์ด ์˜ค๋ฅ˜๋ฅผ ๋‹ค์‹œ panic์œผ๋กœ ์ฒ˜๋ฆฌํ•œ๋‹ค. ๋ณต๊ตฌ๊ฐ€ ํ•„์š”ํ•˜๋ฉด ์˜ค๋ฅ˜์˜ into_inner()๋กœ guard๋ฅผ ๊บผ๋‚ด ๋ฐ์ดํ„ฐ๋ฅผ ํ™•์ธยท์ˆ˜์ •ํ•˜๊ณ  clear_poison()์œผ๋กœ ํ‘œ์‹œ๋ฅผ ์ง€์šธ ์ˆ˜ ์žˆ๋‹ค.


4. Arc<Mutex<T>>: Thread-safe shared mutable state

๋ฐฑ๊ทธ๋ผ์šด๋“œ ํŒŒ์ผ ์ฒ˜๋ฆฌ์—์„œ๋Š” worker๊ฐ€ ํŒŒ์ผ์„ ์ฒ˜๋ฆฌํ•˜๋Š” ๋™์•ˆ main์ด ์ง„ํ–‰ ์ƒํƒœ๋ฅผ ํ‘œ์‹œํ•˜๊ณ  ์ทจ์†Œ ์š”์ฒญ์„ ๋ฐ›๋Š”๋‹ค. ์ž‘์—…์„ ์‹œ์ž‘ํ•˜๋Š” start_job()์€ worker์˜ ์™„๋ฃŒ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๊ณ  ๋ฐ˜ํ™˜ํ•ด์•ผ ํ•œ๋‹ค. main์€ ๊ทธ ๋’ค์—๋„ ๋‹ค๋ฅธ ์ž‘์—…์„ ํ•˜๋ฉด์„œ ์ฒ˜๋ฆฌ ๊ฐœ์ˆ˜๋ฅผ ์ฝ๊ฑฐ๋‚˜ ์ทจ์†Œ๋ฅผ ์š”์ฒญํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค.

์•ž์˜ thread::scope ์˜ˆ์ œ๋Š” scope๋ฅผ ๋ฒ—์–ด๋‚˜๊ธฐ ์ „์— worker์˜ ์ข…๋ฃŒ๋ฅผ ๊ธฐ๋‹ค๋ ธ๋‹ค. ์ด ๋ฐฉ์‹์„ start_job() ์•ˆ์— ์“ฐ๋ฉด main์ด ์ƒํƒœ๋ฅผ ์ „๋‹ฌ๋ฐ›์„ ๋•Œ๋Š” ์ž‘์—…์ด ์ด๋ฏธ ๋๋‚˜ ์žˆ๋‹ค. thread::spawn์œผ๋กœ ๋งŒ๋“  worker๋Š” ์ž์‹ ์„ ์‹œ์ž‘ํ•œ ํ•จ์ˆ˜๋ณด๋‹ค ์˜ค๋ž˜ ์‹คํ–‰๋  ์ˆ˜ ์žˆ๋‹ค.

threads can outlive the lifetime they have been created in.

ํ•จ์ˆ˜ ๋ฐ˜ํ™˜ ๋’ค์—๋„ worker๊ฐ€ ์‚ฌ์šฉํ•  ์ƒํƒœ๋ฅผ ํ•จ์ˆ˜์˜ local variable์— ๋Œ€ํ•œ ์ฐธ์กฐ๋กœ ๋นŒ๋ ค์ค„ ์ˆ˜๋Š” ์—†๋‹ค. ๋‹ค์Œ ์˜ˆ์ œ์—์„œ๋Š” Arc<Mutex<JobState>>๋ฅผ ๋งŒ๋“ค์–ด main๊ณผ worker๊ฐ€ ๊ฐ™์€ ์ƒํƒœ๋ฅผ ํ•จ๊ป˜ ์†Œ์œ ํ•œ๋‹ค.

  • Arc: ๊ฐ™์€ heap ํ• ๋‹น์„ ๊ฐ€๋ฆฌํ‚ค๋Š” Arc๋ฅผ main๊ณผ worker์— ํ•˜๋‚˜์”ฉ ์ค€๋‹ค. start_job()์ด ๋ฐ˜ํ™˜๋œ ๋’ค์—๋„ ๋‘ ์†Œ์œ ์ž๊ฐ€ ๊ฐ์ž ์ƒํƒœ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • Mutex: worker์˜ ์ฒ˜๋ฆฌ ๊ฐœ์ˆ˜ ๊ฐฑ์‹ ๊ณผ main์˜ ์ง„ํ–‰ ์ƒํƒœ ์กฐํšŒยท์ทจ์†Œ ์š”์ฒญ์„ ๋™๊ธฐํ™”ํ•œ๋‹ค. ์—ฌ๋Ÿฌ Arc๊ฐ€ ๊ฐ™์€ JobState๋ฅผ ๊ณต์œ ํ•˜๋Š” ๋™์•ˆ์—๋Š” &mut JobState๋ฅผ ์–ป์„ ์ˆ˜ ์—†์œผ๋ฏ€๋กœ Mutex์˜ guard๋กœ ์ˆ˜์ •ํ•œ๋‹ค.

์˜ˆ์ œ๋Š” ์‹ค์ œ ํŒŒ์ผ I/O ๋Œ€์‹  sleep()์œผ๋กœ ํŒŒ์ผ ํ•˜๋‚˜์˜ ์ฒ˜๋ฆฌ ์‹œ๊ฐ„์„ ํ‰๋‚ด ๋‚ธ๋‹ค. ํŒŒ์ผ ์ฒ˜๋ฆฌ๋Š” lock์„ ๋ณด์œ ํ•˜์ง€ ์•Š์€ ์ƒํƒœ์—์„œ ์ˆ˜ํ–‰ํ•˜๊ณ  ์ƒํƒœ๋ฅผ ํ™•์ธํ•˜๊ฑฐ๋‚˜ ๋ฐ”๊พธ๋Š” ๋™์•ˆ๋งŒ lock์„ ์–ป๋Š”๋‹ค.

use std::sync::{Arc, Mutex};
use std::thread::{self, JoinHandle};
use std::time::Duration;
 
struct JobState {
    completed: usize,
    cancelled: bool,
}
 
fn start_job() -> (Arc<Mutex<JobState>>, JoinHandle<()>) {
    let state = Arc::new(Mutex::new(JobState {
        completed: 0,
        cancelled: false,
    }));
    let worker_state = Arc::clone(&state);
 
    let worker = thread::spawn(move || {
        for _ in 0..100 {
            if worker_state.lock().unwrap().cancelled {
                break;
            }
 
            // ํŒŒ์ผ ํ•˜๋‚˜๋ฅผ ์ฒ˜๋ฆฌํ•œ๋‹ค๊ณ  ๊ฐ€์ •. ์ด ๋™์•ˆ์—๋Š” lock์„ ๋ณด์œ ํ•˜์ง€ ์•Š๋Š”๋‹ค.
            thread::sleep(Duration::from_millis(10));
            worker_state.lock().unwrap().completed += 1;
        }
    });
 
    (state, worker) // worker์˜ ์ข…๋ฃŒ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๊ณ  ์†Œ์œ ๊ถŒ์„ ํ˜ธ์ถœ์ž์—๊ฒŒ ๋ฐ˜ํ™˜
}
 
fn main() {
    let (state, worker) = start_job();
 
    {
        let snapshot = state.lock().unwrap();
        println!("์ฒ˜๋ฆฌํ•œ ํŒŒ์ผ: {}", snapshot.completed);
        // ๋„์‹ ์‹œ์  โ‘ : main์ด lock์„ ๋ณด์œ ํ•˜๊ณ  worker๊ฐ€ ๋‹ค์Œ lock์„ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๊ฐ€๋Šฅํ•œ ์‹œ์ 
    } // snapshot์ด drop๋˜๋ฉฐ unlock
 
    state.lock().unwrap().cancelled = true;
    worker.join().unwrap();
    // ๋„์‹ ์‹œ์  โ‘ก: worker ์ข…๋ฃŒ ๋’ค, ์•„๋ž˜ ๊ฒ€์‚ฌ์šฉ lock์„ ์–ป๊ธฐ ์ „
 
    assert!(state.lock().unwrap().cancelled);
    assert_eq!(Arc::strong_count(&state), 1);
}

start_job()์˜ state์™€ worker๋Š” ๋ฐ˜ํ™˜๊ฐ’์œผ๋กœ ์ด๋™ํ•˜๊ณ  worker_state๋Š” worker์˜ closure๋กœ ์ด๋™ํ•œ๋‹ค. worker๋Š” ํ•จ์ˆ˜ ์•ˆ์˜ local variable์„ ๋นŒ๋ฆฌ์ง€ ์•Š์œผ๋ฏ€๋กœ ํ•จ์ˆ˜๊ฐ€ ๋ฐ˜ํ™˜๋œ ๋’ค์—๋„ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค. ์ถœ๋ ฅ๋˜๋Š” ์ฒ˜๋ฆฌ ๊ฐœ์ˆ˜๋Š” ๋‘ thread์˜ ์‹คํ–‰ ์ˆœ์„œ์— ๋”ฐ๋ผ ๋‹ฌ๋ผ์ง„๋‹ค.

์‹œ์  โ‘ ์€ main์ด snapshot์„ ๋ณด์œ ํ•˜๊ณ  worker๊ฐ€ ์ƒํƒœ ํ™•์ธ์ด๋‚˜ ๊ฐฑ์‹ ์„ ์œ„ํ•œ lock์—์„œ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๊ฐ€๋Šฅํ•œ ์ƒํƒœ๋‹ค. n์€ ์ด๋•Œ๊นŒ์ง€ ์ฒ˜๋ฆฌํ•œ ํŒŒ์ผ ๊ฐœ์ˆ˜๋‹ค.

flowchart TB
    subgraph B_MAIN["Main thread์˜ Stack / start_job ๋ฐ˜ํ™˜ ๋’ค"]
        B_STATE["state: Arc&lt;Mutex&lt;JobState&gt;&gt;"]
        B_SNAPSHOT["snapshot: MutexGuard&lt;JobState&gt;"]
        B_JOIN["worker: JoinHandle"]
    end
    subgraph B_WORKER["Worker thread์˜ Stack"]
        B_WORKER_STATE["worker_state: Arc&lt;Mutex&lt;JobState&gt;&gt;"]
        B_WAIT["lock์—์„œ ๋Œ€๊ธฐ / guard ์ƒ์„ฑ ์ „"]
    end
    subgraph B_HEAP["๊ณต์œ  Heap"]
        subgraph B_ALLOCATION["Arc ํ• ๋‹น ํ•˜๋‚˜"]
            B_COUNT["atomic strong count: 2"]
            subgraph B_MUTEX["Mutex&lt;JobState&gt;"]
                B_LOCK["lock: ์ž ๊น€ / main์ด ๋ณด์œ "]
                B_DATA["JobState<br/>completed: n<br/>cancelled: false"]
            end
        end
    end
    B_STATE -->|๊ณต์œ  ์†Œ์œ | B_ALLOCATION
    B_WORKER_STATE -->|๊ณต์œ  ์†Œ์œ | B_ALLOCATION
    B_SNAPSHOT -->|Mutex ์ฐธ์กฐ| B_MUTEX
    B_SNAPSHOT -.->|drop ์‹œ unlock| B_LOCK
    B_WAIT -.->|ํš๋“ ๋Œ€๊ธฐ| B_LOCK
  • ๊ณต์œ  ์†Œ์œ ๊ถŒ
    • main๊ณผ worker์˜ Arc๊ฐ€ ๊ฐ™์€ Mutex์™€ JobState ํ•˜๋‚˜๋ฅผ ์†Œ์œ 
    • snapshot์„ ์–ป๊ฑฐ๋‚˜ dropํ•ด๋„ strong count๋Š” ๋ฐ”๋€Œ์ง€ ์•Š์Œ
  • ์ž ๊ธˆ ๋ฒ”์œ„
    • snapshot์ด ์žˆ๋Š” ๋™์•ˆ worker๋Š” ๊ฐ™์€ Mutex์˜ lock์„ ์–ป์„ ์ˆ˜ ์—†์Œ
    • main์€ ์ด guard๋ฅผ ๋จผ์ € dropํ•œ ๋’ค ์ƒˆ ์ž„์‹œ guard๋กœ cancelled๋ฅผ ๋ณ€๊ฒฝ
  • ์ทจ์†Œ ํ™•์ธ
    • worker๋Š” ๋ฐ˜๋ณต์„ ์‹œ์ž‘ํ•  ๋•Œ cancelled๋ฅผ ํ™•์ธ
    • ์ด๋ฏธ ์‹œ์ž‘ํ•œ ํŒŒ์ผ ์ฒ˜๋ฆฌ๋Š” ๋๋‚  ์ˆ˜ ์žˆ์œผ๋ฉฐ ์ทจ์†Œ ์š”์ฒญ์„ ํ™•์ธํ•˜๋ฉด ๋‹ค์Œ ํŒŒ์ผ์„ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š๊ณ  ์ข…๋ฃŒ

์‹œ์  โ‘ก์—์„œ๋Š” join()์œผ๋กœ worker์˜ ์ข…๋ฃŒ๋ฅผ ํ™•์ธํ–ˆ๋‹ค. worker์˜ Arc๊ฐ€ drop๋˜์–ด main์˜ Arc๋งŒ ๋‚จ๊ณ  m์€ ์ตœ์ข… ์ฒ˜๋ฆฌ ๊ฐœ์ˆ˜๋‹ค.

flowchart TB
    subgraph C_MAIN["Main thread์˜ Stack"]
        C_STATE["state: Arc&lt;Mutex&lt;JobState&gt;&gt;"]
    end
    C_WORKER["Worker thread ์ข…๋ฃŒ<br/>worker_state drop ์™„๋ฃŒ"]
    subgraph C_HEAP["๊ณต์œ  Heap"]
        subgraph C_ALLOCATION["Arc ํ• ๋‹น ํ•˜๋‚˜"]
            C_COUNT["atomic strong count: 1"]
            subgraph C_MUTEX["Mutex&lt;JobState&gt;"]
                C_LOCK["lock: ํ’€๋ฆผ"]
                C_DATA["JobState<br/>completed: m<br/>cancelled: true"]
            end
        end
    end
    C_STATE -->|๊ณต์œ  ์†Œ์œ | C_ALLOCATION
  • ๋‚จ์€ ์†Œ์œ ์ž
    • join()์€ worker๋ฅผ ์†Œ๋น„ํ•˜๋ฏ€๋กœ main์˜ Stack์—๋Š” JoinHandle์ด ๋‚จ์ง€ ์•Š๊ณ  ์ƒํƒœ๋Š” main์˜ state๊ฐ€ ๊ณ„์† ์†Œ์œ 
  • ๋ฐ˜๋‚ฉ ์‹œ์ 
    • worker๊ฐ€ ๋๋‚˜๋„ main์€ ์ƒํƒœ๋ฅผ ์ฝ์„ ์ˆ˜ ์žˆ์Œ
    • ๋งˆ์ง€๋ง‰ Arc๊ฐ€ drop๋  ๋•Œ Mutex์™€ ๋‚ด๋ถ€ JobState๊ฐ€ drop

5. ํƒ€์ž… ์„ ํƒ๊ณผ thread ๊ณต์œ  ์กฐ๊ฑด

ํƒ€์ž…๊ณต์œ  ์†Œ์œ ๊ถŒ๋‚ด๋ถ€ T์˜ ์œ„์น˜์ˆ˜์ • ๊ฒฝ๋กœthread ๊ฐ„ ๊ณต์œ  ์กฐ๊ฑด
Arc<T>์ œ๊ณตArc์˜ heap ํ• ๋‹น ์•ˆget_mutยทmake_mut ๋˜๋Š” T์˜ ๋™๊ธฐํ™” APIT: Send + Sync
Mutex<T>์ œ๊ณต ์•ˆ ํ•จMutex ์ž์‹ ์˜ ํ•„๋“œlockยทtry_lock์˜ guardT: Send
Arc<Mutex<T>>์ œ๊ณตArc ํ• ๋‹น ์•ˆ์˜ Mutex ํ•„๋“œMutex์˜ guardT: Send
  • Send๋Š” ๊ฐ’์˜ ์†Œ์œ ๊ถŒ์„ ๋‹ค๋ฅธ thread๋กœ ๋„˜๊ฒจ๋„ ์•ˆ์ „ํ•จ์„ ๋‚˜ํƒ€๋‚ธ๋‹ค.
  • Sync๋Š” ๊ณต์œ  ์ฐธ์กฐ &T๋ฅผ thread ์‚ฌ์ด์— ๋„˜๊ฒจ๋„ ์•ˆ์ „ํ•จ์„ ๋‚˜ํƒ€๋‚ธ๋‹ค. T: Sync์˜ ์ •์˜๋Š” &T: Send๋‹ค.

Mutex<T>๋Š” T: Send์ด๋ฉด Send์™€ Sync๋ฅผ ๊ตฌํ˜„ํ•œ๋‹ค. guard๋กœ T๋ฅผ ๊ต์ฒดํ•ด ๊บผ๋‚ด๋Š” thread๊ฐ€ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ T๋Š” thread ์‚ฌ์ด์—์„œ ์ด๋™ํ•  ์ˆ˜ ์žˆ์–ด์•ผ ํ•œ๋‹ค. lock์„ ์–ป์€ thread์— ์ฐจ๋ก€๋กœ ๋ฐฐํƒ€์  ์ ‘๊ทผ์„ ์ œ๊ณตํ•˜๋ฏ€๋กœ T ์ž์ฒด์˜ Sync๋Š” ์š”๊ตฌํ•˜์ง€ ์•Š๋Š”๋‹ค.

Arc ์•ˆ์˜ ํƒ€์ž…์ด Mutex<T>์ผ ๋•Œ๋Š” T: Send๋งŒ์œผ๋กœ Mutex๊ฐ€ Send + Sync๋ฅผ ๋งŒ์กฑํ•˜๋ฏ€๋กœ Arc์˜ ๊ณต์œ  ์กฐ๊ฑด๋„ ์ถฉ์กฑํ•œ๋‹ค.

ํ•„์š”ํ•œ ๋™์ž‘์„ ํƒํ•  ์ˆ˜ ์žˆ๋Š” ํƒ€์ž…
์—ฌ๋Ÿฌ thread๊ฐ€ ๊ฐ™์€ ๋ฐ์ดํ„ฐ๋ฅผ ์†Œ์œ ํ•˜๋ฉฐ ์ฝ๊ธฐArc<T>
์ˆ˜๋ช…์„ ๋‹ค๋ฅธ ๋ฐฉ์‹์œผ๋กœ ํ™•๋ณดํ•œ ๊ฐ’์— ์—ฌ๋Ÿฌ thread๊ฐ€ lock์œผ๋กœ ์ ‘๊ทผMutex<T>์™€ ๊ณต์œ  ์ฐธ์กฐ
์—ฌ๋Ÿฌ thread๊ฐ€ ๊ฐ™์€ ๊ฐ’์„ ์†Œ์œ ํ•˜๋ฉฐ lock์œผ๋กœ ์ˆ˜์ •Arc<Mutex<T>>

Arc<RefCell<T>>๋Š” RefCell<T>๊ฐ€ Sync๊ฐ€ ์•„๋‹ˆ๋ฏ€๋กœ thread ๊ฐ„ ๊ณต์œ ์— ์“ธ ์ˆ˜ ์—†๋‹ค. Arc<Mutex<Rc<T>>>๋„ ๋‚ด๋ถ€ Rc๊ฐ€ Send๊ฐ€ ์•„๋‹ˆ์–ด์„œ ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜์ง€ ์•Š๋Š”๋‹ค. RefCell์€ T๊ฐ€ Send์ด๋ฉด ์†Œ์œ ๊ถŒ์„ ๋‹ค๋ฅธ thread๋กœ ์˜ฎ๊ธธ ์ˆ˜ ์žˆ์ง€๋งŒ ๊ฐ™์€ RefCell์„ ์—ฌ๋Ÿฌ thread๊ฐ€ ๊ณต์œ ํ•˜๋„๋ก ๋™๊ธฐํ™”ํ•˜์ง€๋Š” ์•Š๋Š”๋‹ค.

๊ฐ’์˜ ์ˆ˜๋ช…์„ ๋นŒ๋ฆผ์œผ๋กœ ํ‘œํ˜„ํ•  ์ˆ˜ ์žˆ์œผ๋ฉด ๊ณต์œ  ์†Œ์œ ๊ถŒ์„ ์ถ”๊ฐ€ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค. ๊ฐ™์€ thread ์•ˆ์—์„œ ์†Œ์œ ๊ถŒ๊ณผ ๊ฐ€๋ณ€ ์ ‘๊ทผ์„ ์กฐํ•ฉํ•  ๋•Œ๋Š” Rc์™€ RefCell์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.