뮤텍스 (Mutex) 란 무엇인가?
Mutex란 공유된 자원의 데이터 혹은 임계영역(Critical Section) 등에
하나의 Process 혹은 Thread가 접근하는 것을 막아주는(동기화 대상이 하나)
임계구역(Critical Section)을 가진 스레드들의 실행시간(Running Time)이
서로 겹치지 않고 각각 단독으로 실행(Mutual Exclusion) 되도록 하는 기술이다.
뮤텍스는 세마포어의 한 종류로써,
바이너리 세마포어 (binary semaphore) 라고 불리기도 한다.
뮤텍스 (Mutex) 의 작동 방식
1) Locking
스레드가 뮤텍스를 획득하려고 할 때,
뮤텍스가 이미 다른 스레드에 의해 잠겨 있지 않다면,
해당 스레드는 뮤텍스를 잠그고 자원에 접근한다.
Mutex Lock 을 획득한 스레드는 해당 공유자원에 대해서,
읽기 / 쓰기 모두 가능하다.
반면 Mutex Lock 을 얻지 못한 스레드는
해당 공유자원의 Mutex Lock 이 Unlock 될때까지 대기한다.
2) Unlocking
자원 사용이 끝난 스레드는 뮤텍스를 해제한다.
이는 다른 스레드가 해당 공유자원에 접근할 수 있도록 만든다.
3) Ownership
RUST 에서 뮤텍스는 소유권 개념을 가지고 있다.
즉, 뮤텍스를 잠근 스레드만이 뮤텍스를 해제할 수 있다.
뮤텍스 (Mutex) 의 사용 예제
예를들어 공유자원 i32 형 0 을 10개의 스레드에서 1씩 추가해주는 로직을 봐주자.
Mutex 를 사용하지 않고 단지 Arc 만 사용해서 자원을 공유해준다고 가정해보자.
async fn main() {
// 커스텀 로거
set_global_logger();
let data = Arc::new(0);
let mut handles = vec![];
for i in 0..10 {
let data_clone = Arc::clone(&data);
let handle = thread::Builder::new()
.name(format!("thread-{}", i)) // 스레드에 이름 지정
.spawn(move || {
let mut num = *data_clone;
info!("Thread {}: Value before increment: {}", i, num);
// 데이터를 수정합니다.
num += 1;
info!("Thread {}: Value after increment: {}", i, num);
})
.unwrap();
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
// 최종 결과를 출력합니다.
println!("Final value: {}", *data);
}
결과는 0 이 나온다.
그리고 로그 파일을 보면 아래와 같다.
Arc는 스레드 안전한 참조 카운팅 포인터로,
여러 스레드 간에 데이터를 공유할 수 있게 해주지만,
그 자체로 데이터의 동기화나 변경은 처리하지 않는다.
Arc는 단순히 데이터에 대한 소유권을 안전하게 공유하는 역할을 한다.
데이터의 동기화나 변경까지 처리하려면 아래와 같이
Arc + Mutex 조합을 사용할 수 있다.
async fn main() {
// 커스텀 로거
set_global_logger();
let data = Arc::new(Mutex::new(0));
let mut handles = vec![];
for i in 0..10 {
let data_clone = Arc::clone(&data);
let handle = thread::Builder::new()
.name(format!("thread-{}", i)) // 스레드에 이름 지정
.spawn(move || {
let mut num = data_clone.lock().unwrap();
info!("Thread {}: Value before increment: {}", i, num);
// 데이터를 수정합니다.
*num += 1;
info!("Thread {}: Value after increment: {}", i, num);
})
.unwrap();
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
// 최종 결과를 출력합니다.
println!("Final value: {}", *data.lock().unwrap());
}
결과는 원한대로 10이 나오게 된다.
로그파일을 살펴보면 아래와 같다.
'RUST' 카테고리의 다른 글
[RUST] RWLock (Read-Write Lock) (0) | 2024.05.05 |
---|---|
[RUST] Semaphore (세마포어) (1) | 2024.05.02 |
[RUST] Rc, Arc 란 (1) | 2024.04.30 |
[RUST] 트레이트 (Traits) (0) | 2024.01.23 |
[RUST] 제네릭 (Generics) (0) | 2024.01.22 |