DeadLock이란?
둘 이상의 프로세스 혹은 스레드가 상대방이 점유한 자원을 대기하며 무한정 기다리는 상황이다.
발생조건
- 상호배제(Mutual Exclusion) - 자원은 하나의 프로세스만 사용 할 수 있다.
- 점유대기(Hold and Wait) - 최소한 하나의 자원을 점유하고 있으면서 다른 프로세스에 할당되어 사용하고 있는 자원을 추가로 점유하기 위해 대기중이어야 한다.
- 비선점(No Preemption) - 다른 프로세스에 할당된 자원은 사용이 끝날때까지 강제로 빼앗을 수 없다.
- 순환 대기(Circular Wait) - 프로세스들이 서로 자원을 기다리며 순환적인 대기 관계여야 한다.
SpinLock이란?
경쟁 상태 상황에서 Lock이 반환될때까지 즉 임계영역에 진입 가능할 때까지 프로세스가 계속 재시도하며 대기하는 상태이다.
스핀락은 context switching이 일어나지 않아 CPU의 부하를 줄일 수 있다는 장점이 있지만, lock이 반환 될때까지 루프를 돌면서 계속 확인해야 한다는 단점이 있다.
구현을 위해서 CAS에 대해서 살펴보자.
CAS(Compare - And - Swap) 연산이란?
atomic하게 이루어지는 연산으로특정 메모리위치의 값이 주어진 값과 동일하다면 해당 메모리 주소를 새로운 값으로 대체해주는 연산이다.
- compare_exchange_weak - 실패할 경우 자동으로 다시 시도할 수 있는 연산이다. 실패가 자주 발생할 수 있는 환경에서 유용
- compare_exchange_strong - 실패시 자동으로 시도하지 않으며 호출자가 연산을 재시도해야한다. 하지만 weak보다 더 강력한 처리를 제공한다.
class SpinLock
{
public:
void lock()
{
bool expected = false;
bool desired = true;
while(_locked.compare_exchange_strong(expected, desired) == false)
{
expected = false;
}
void unlock()
{
_locked.store(false);
}
}
Sleep Lock이란?
스레드가 자원을 얻을 수 없을 때, CPU를 낭비하지 않고 Sleep 상태로 전환되어 다른 스레드가 실행 될 수 있도록 하는 방식이다. 일정 시간마다 자원을 획득할 수 있는지 확인하며 기다린다. SpinLock과는 다르게 시스템 콜을 호출하여 컨텍스트 스위칭이 일어난다.
class SpinLock
{
public:
void lock()
{
bool expected = false;
bool desired = true;
while(_locked.compare_exchange_strong(expected, desired) == false)
{
expected = false;
this_thread::sleep_for(3ms);
//this_thread::sleep_for(std::chrono::milliseconds(300));
}
void unlock()
{
_locked.store(false);
}
}
Event Lock이란?
스레드가 자원을 얻을 수 없을 때 특정 이벤트가 발생할 때까지 대기하거나, 특정 조건이 충족되었을때 스레드를 깨워 실행 될 수 있도록 하는 방식이다. 이벤트 객체를 사용하여 동작한다.
대기 상태로 들어갈 때, 이벤트를 받고 깨워져 실행 상태로 전환될 때 컨텍스트 스위칭이 발생한다.
void Producer()
{
while(true)
{
{
unique_lock<mutex> lock(m);
q.push(100);
}
SetEvent(handle);
this_thread::sleep_for(10000ms);
}
}
void Consumer()
{
while(true)
{
WaitForSingleObject(handle, INFINITE);
unique_lock<mutex> lock(m);
if(q.empty() == false)
{
int data = q.front();
q.pop();
}
}
}
int main()
{
handle = CreateEvent(NULL/*보안속성*/,FALSE/*bManualReset*/,FALSE/*bInitialState*/,NULL);
thread t1(Producer);
thread t2(Consumer);
t1.join();
t2.join();
}
- bManualReset - 이벤트 객체가 자동리셋인지 수동리셋인지 지정
- TRUE - 수동 리셋, 이벤트가 신호 상태로 변경된 후 명시적으로 ResetEvent를 호출해 비신호 상태로 돌아
- FALSE - 자동 리셋, 이벤트 객체가 신호 상태로 변경되면, 하나의 스레드를 깨운 후 자동으로 비신호 상태로 돌아감
- bInitialState - 이벤트 객체가 생성될때 초기 상태를 설정
- TRUE - 이벤트 객체는 신호 상태로 생성, 스레드가 즉시 실행
- FALSE - 비신호 상태로 생성, 신호 상태가 될때까지 대기
'서버' 카테고리의 다른 글
std::future (0) | 2025.04.08 |
---|---|
조건 변수 Condition Variable (0) | 2025.04.08 |
스레드와 멀티스레드 (1) | 2025.04.05 |