Rust, Thread

Jeonghak Cho·2025년 1월 5일

Rust

목록 보기
9/13

Thread 개요

  • 프로세스 간 자원은 완벽히 격리되고 직접 통신이 불가
  • 하나의 프로세스로 부터 파생되는 쓰레드는 동일한 자원(메로리)에 접근 가능
  • 프로그램은 항시 하나의 Main 쓰레드로 부터 시작, Main 쓰레드가 main() 함수 실행
  • std::thread::spawn 표준함수에 함수명을 전달하여 쓰레드 실행
  • Main 쓰레드는 모든 실행 쓰레드 처리 확인 후 종료하기 위해 JoinHandle을 사용

Spawn 함수

함수명 f를 spawn 함수에 전달하여 새로운 쓰레드를 실행한다.

fn main() {
    thread::spawn(f);
    thread::spawn(f);

    println!("Hello main");
}

fn f() {
    println!("  Hello function");

    let id = thread::current().id();
    println!("  Hello thread: {id:?}");
}

Main 쓰레드가 자식 쓰레드 보다 먼저 종료되어 자식쓰레드의 출력 작업이 끝까지 수행되지 못한다.

root@DESKTOP-SCOK45O:~/mythread# cargo run
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.01s
     Running `target/debug/mythread`
Hello main
  Hello function
  Hello thread: ThreadId(2)
  Hello function
  Hello thread: ThreadId(3)

root@DESKTOP-SCOK45O:~/mythread# cargo run
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.00s
     Running `target/debug/mythread`
Hello main
  Hello function
  Hello thread: ThreadId

root@DESKTOP-SCOK45O:~/mythread# cargo run
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.00s
     Running `target/debug/mythread`
Hello main
  Hello function
  Hello thread: ThreadId(
    
root@DESKTOP-SCOK45O:~/mythread# cargo run
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.00s
     Running `target/debug/mythread`
Hello main
  Hello function
  Hello thread: 

JOIN HANDLE 사용

Main 쓰레드가 먼저 종료되지 못하도록

fn main() {
    let t1 = thread::spawn(f);
    let t2 = thread::spawn(f);

    println!("Hello main");

    t1.join().unwrap();
    t2.join().unwrap();
}

fn f() {
    println!("  Hello function");

    let id = thread::current().id();
    println!("  Hello thread: {id:?}");
}

쓰레드 Lock

ThreadId(2), ThreadId(3) 이 반드시 실행되도록 보장되었다. 하지만 쓰레드의 잠금 단위가 함수 단위가 아니고 println! 단위이므로 각 함수의 출력 결과는 섞일 수 있다.

root@DESKTOP-SCOK45O:~/mythread# cargo run
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.01s
     Running `target/debug/mythread`
Hello main
  Hello function
  Hello thread: ThreadId(3)
  Hello function
  Hello thread: ThreadId(2)

root@DESKTOP-SCOK45O:~/mythread# cargo run
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.01s
     Running `target/debug/mythread`
Hello main
  Hello function
  Hello function
  Hello thread: ThreadId(3)
  Hello thread: ThreadId(2)

스태틱 변수에 대해 여러 쓰레드가 접근하는 방식은 컴파일 시점에 오류를 발생시킨다.

use std::thread;

fn main() {
    static mut n : u32 = 0;
    thread::scope(|s| {
        for _ in 0..10 {
            s.spawn(move || {
                let id = thread::current().id();
                for _ in 0..100 {
                    n += 1;
                }
                println!("Thread - {id:?} n - {n:}");
            });
        }
    });
    assert_eq!(n, 1000);
}

오류 내용

root@DESKTOP-SCOK45O:~/mythread# cargo run 
   Compiling mythread v0.1.0 (/root/mythread)
error[E0133]: use of mutable static is unsafe and requires unsafe function or block
  --> src/main.rs:10:21
   |
10 |                     n += 1;
   |                     ^ use of mutable static
   |
   = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior

error[E0133]: use of mutable static is unsafe and requires unsafe function or block
  --> src/main.rs:12:48
   |
12 |                 println!("Thread - {id:?} n - {n:}");
   |                                                ^ use of mutable static
   |
   = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior

error[E0133]: use of mutable static is unsafe and requires unsafe function or block
  --> src/main.rs:16:16
   |
16 |     assert_eq!(n, 1000);
   |                ^ use of mutable static
   |
   = note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior

For more information about this error, try `rustc --explain E0133`.
error: could not compile `mythread` (bin "mythread") due to 3 previous errors

Mutax

여러 쓰레드가 접근하는 공유 변수를 Mutax로 보호한다.

use std::sync::Mutex;
use std::thread;

fn main() {
    let n = Mutex::new(0);
    thread::scope(|s| {
        for _ in 0..10 {
            s.spawn(|| {
                let id = thread::current().id();
                
                let mut guard = n.lock().unwrap();
                for _ in 0..100 {
                    *guard += 1;
                }

                println!("Thread - {id:?},  guard - {guard:?}");
            });
        }
    });
    assert_eq!(n.into_inner().unwrap(), 1000);
}

정상 수행 결과는 assert 에서 기대하는 값과 일치한다. guard 변수는 범위 내에서 유효하며 범위를 벗어나면 자연스럽게 사라진다. 모든 쓰레드 작업 후 into_inner() 를 사용해서 뮤텍스의 소유권을 가져온다. 이를 통해 뮤텍스의 잠금은 자연스럽게 해제된다.

root@DESKTOP-SCOK45O:~/mythread# cargo run 
   Compiling mythread v0.1.0 (/root/mythread)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.44s
     Running `target/debug/mythread`
Thread - ThreadId(2),  guard - 100
Thread - ThreadId(3),  guard - 200
Thread - ThreadId(4),  guard - 300
Thread - ThreadId(5),  guard - 400
Thread - ThreadId(6),  guard - 500
Thread - ThreadId(7),  guard - 600
Thread - ThreadId(8),  guard - 700
Thread - ThreadId(10),  guard - 800
Thread - ThreadId(9),  guard - 900
Thread - ThreadId(11),  guard - 1000

0개의 댓글