1. 경쟁 상태 (Race condition)
경쟁 상태는 둘 이상의 스레드가 공유 데이터에 동시에 접근하여 예측할 수 없는 결과를 초래하는 상황을 말한다.
ex)
#include <iostream>
#include <thread>
int num = 0;
void Increment() {
for (int i = 0; i < 1000000; i++) {
num++; // race condition을 유발할 수 있다.
}
}
int main() {
std::thread t1(Increment);
std::thread t2(Increment);
t1.join();
t2.join();
std::cout << "값: " << num << std::endl;
// 예상 값은 2000000이지만, 실제로는 이보다 작은 값이 출력된다.
}
위 예시에서 `num`은 race condition의 대상이 된다.
두 스레드가 동시에 이 변수를 증가시키려고 하면, 일부 연산이 손실될 수 있다.
2. 임계 영역(Critical Section)
임계 영역은 여러 스레드가 동시에 접근해서는 안 되는 공유 자원을 접근하는 코드 영역을 말한다.
임계 영역에 대한 접근은 상호 배제(mutual exclusion)를 통해 동기화되어야 한다.
임계 영역의 특성 :
- 한 번에 하나의 스레드만 임계 영역에 진입할 수 있어야 함
- 임계 영역 밖의 스레드는 다른 스레드의 임계 영역 진입을 막을 수 없어야 함
- 임계 영역에 진입하려는 스레드의 대기 시간이 유한해야 함(기아 상태 방지)
3. 동기화의 필요성
동기화가 필요한 이유 :
- 데이터 일관성 유지 : 여러 스레드가 동시에 데이터를 수정할 때 데이터의 일관성을 보장
- Race condition 방지 : 예측 가능하고 정확한 결과를 보장
- 교착 상태(Deadlock) 방지 : 적절한 동기화 기법을 사용하여 교착 상태를 예방
- 원자성(Atomicity) 보장 : 특정 연산이 중간에 끊기지 않고 완전히 수행되도록 함
4. C++에서의 동기화:
1) std::mutex:
#include <iostream>
#include <mutex>
#include <thread>
int num = 0;
std::mutex mtx;
void Increment() {
for (int i = 0; i < 1000000; i++) {
std::lock_guard<std::mutex> lock(mtx);
num++;
}
}
int main() {
std::thread t1(Increment);
std::thread t2(Increment);
t1.join();
t2.join();
std::cout << "값: " << num << std::endl;
// 이제 항상 2000000이 출력된다.
}
2) std::atomic:
#include <iostream>
#include <atomic>
#include <thread>
std::atomic<int> num(0);
void Increment() {
for (int i = 0; i < 1000000; i++) {
num++;
}
}
int main() {
std::thread t1(Increment);
std::thread t2(Increment);
t1.join();
t2.join();
std::cout << "값: " << num << std::endl;
// 이제 항상 2000000이 출력된다.
}
동기화를 통해 race condition을 방지하고 데이터의 일관성을 유지할 수 있다.
하지만 과도한 동기화는 성능 저하를 일으킬 수 있으므로, 사용에 주의해야 한다.
'멀티스레딩' 카테고리의 다른 글
동기화 기법: Atomic 연산 (0) | 2024.10.05 |
---|---|
메모리 구조와 멀티스레딩 (0) | 2024.10.03 |
스레드 생성과 관리 (0) | 2024.09.28 |
멀티스레딩 기초 (0) | 2024.09.28 |
개요 (0) | 2024.09.26 |