동기화 도구

박태현·2025년 6월 8일
0

예약 프로젝트

목록 보기
2/8

시스템 성능 향상 시키기 위해 멀티코어 시스템을 사용하여 여러 프로세스가 동시에 실행되게 하지만 이러한 환경에서는 하나의 공유 자원을 여러 프로세스가 동시에 접근하려 할 때 문제가 발생할 수 있음

예를 들어, 한 프로세스가 자원을 사용하고 있는 동안 다른 프로세스가 동시에 같은 자원에 접근해 값을 변경하거나, 값을 읽는 도중 변경이 일어나면 데이터 불일치나 예기치 않은 오류가 발생.. ..

이러한 문제를 동기화 문제라고 하며, 여기서 동시에 접근될 수 있는 공유 자원을 임계 영역이라고 하며, 임계 영역은 반드시 하나의 프로세스만 접근하도록 제어되어야 함

임계 영역


여러 프로세스 또는 스레드 공유 자원을 접근하는 코드 영역 중, 동시에 하나만 실행되어야 하는 부분을 의미

임계 영역의 요구 조건

  1. 상호 배제 ( Mutual Exclution )

    두 개 이상의 프로세스가 동시에 임계 영역에 들어갈 수 없어야 한다

  2. 진행 ( Process )

    임계 영역에 진입하려는 프로세스가 있을 때, 현재 임계 영역을 사용하지 않는다면 적절한 프로세스가 곧바로 진입해야 함

    ⇒ 불필요한 대기 없이 진행이 가능해야 함

  3. 한정 대기 ( Bounded Waiting )

    어떤 프로세스가 임계 영역 진입을 요청한 후, 무한히 대기하는 일을 없어야 함

Lock 매커니즘


스핀락

임계 영역의 락이 해제될 때까지 CPU를 점유하고 계속 루프를 돌며 대기하는 방식

스핀락은 임계 영역에 진입하기 위해 락이 해제될 때까지 의미 없는 루프를 돌며 기다리는 Busy Waiting 방식

하나의 스레드나 프로세스가 CPU를 계속 점유하며 대기하기 때문에 문맥 교환이 발생하지 않음

→ 문맥 교환에 따른 오버헤드가 없다는 점에서 짧은 시간 동안 락을 점유하는 경우엔 효율적일 수 있음

하지만, 임계 영역이 오랫동안 락을 해제하지 않는 경우, 락을 기다리는 스레드가 CPU를 계속 점유하게 되어 오히려 성능 저하와 오버헤드가 발생할 수 있음

스핀락은 상태가 단순히 lock / unlock으로만 구성되어 있기에 한 번에 하나의 스레드만 임계 영역에 접근 가능하며, 락을 건 스레드가 직접 언락을 해야 함

→ 다른 스레드가 대신 해제하는 것은 허용되지 않는다는 의미

이런 구조기에, 스핀락은 멀티 프로세서 시스템에서만 적합함

→ 락을 기다리는 놈이 CPU를 독점해서 락을 가진 놈이 락 해제 실행을 하지 못함 → 교착 상태 발생

Mutex

스핀락과 마찬가지로 lock과 unlock 두 가지로만 존재함

하지만, Busy Waiting 방식을 따르는 스핀락과는 다르게 mutex는 lock이 잠겨 있으면 해당 스레드를 sleep 상태로 전환시켜놨다가, lock이 해제될 때 os로부터 wake up을 받아 다시 lock을 시도함

스레드가 Sleep 상태로 전환되면, OS는 이를 대기 큐에 보관하고, 락 해제나 조건 충족과 같은 특정 이벤트가 발생했을 때 wake up 신호를 통해 해당 스레드를 다시 실행 가능한 상태로 깨움

mutex 또한 lock을 획득한 주체만이 unlock을 진행할 수 있는 매커니즘을 따름

이러한 특성 덕분에, 긴 시간 동안 점유가 필요한 임계 영역이나 CPU 자원을 아끼고 싶은 경우에 적합하며, 주로 스레드 간 동기화에서 많이 사용

[ 과정 ]

  1. 스레드 A가 뮤텍스를 lock→ 임계영역 진입
  2. 스레드 B가 동일한 락을 시도하면 → lock이 이미 잠겨 있으므로 sleep 상태로 대기
  3. 스레드 A가 임계영역 작업을 마치고 lock을 해제
  4. → OS가 대기 중이던 스레드 B를 Wakeup ( signal ) 해서 다시 lock 획득을 시도하게 함

Semaphore

세마포어는 스핀락, 뮤텍스와 달리 정수형 값으로 표기하는 동기화 도구임

이 정수 값을 활용하여 하나 이상의 스레드 또는 컴포넌트가 동시에 공유 자원에 접근할 수 있도록 허용할 수 있음

자원이 여러 개일 때 복수의 스레드가 동시에 접근하되, 그 수를 제어하도록 만들어주는 도구

정수 값은 공유 자원을 수를 의미함


두 가지의 주요한 연산 semWait()와 semSignal()을 통해 임계 영역 진입 및 해제를 관리함

  • semWait() 연산 - 자원 요청 세마포어의 값을 1 감소시킴 감소 결과가 0 이상이면 세마포어의 값을 1 감소시키고, 스레드는 대기하지 않고 바로 임계 영역( 공유 자원 )에 진입하여 작업을 수행 하지만, 음수라면 자원이 부족하다는 의미이므로 해당 스레드는 blocked 상태로 전환되어 대기 큐에 삽입됨
  • semSignal() 연산 - 자원 반납 세마포어의 값을 1 증가시킴 값이 음수였던 경우 대기 큐의 blocked 된 스레드 중 하나를 깨워 다시 자원 접근을 시도할 수 있게 함

세마포어는 단순한 lock 도구가 아니라 Signaling( 알림 ) 메커니즘을 제공

⇒ lock을 소유하지 않은 스레드라도 semSignal()을 호출하여 다른 스레드를 깨울 수 있는 구조임

[ 과정 ]

만약 여러 스레드가 프린터기 3대를 사용한다고 했을 때 이를 세마포어로 조절하는 예제

  1. 초기 세마포어의 값은 3으로 설정 → 동시에 최대 3개의 스레드가 자원을 사용할 수 있다는 의미

  2. 스레드 A가 semWait() 연산을 호출

    → 세마포어 값이 3이므로 1 감소하여 2가 되고, 스레드 A는 자원에 접근하여 작업을 수행

  3. 스레드 B가 semWait()을 호출

    → 현재 세마포어 값은 2이므로 다시 1 감소하여 1이 되고, 스레드 B도 자원에 접근하여 작업을 수행

  4. 스레드 C도 semWait()을 호출

    → 세마포어 값이 1에서 0으로 감소하고, 스레드 C도 자원을 사용

  5. 이제 세마포어 값은 0이므로

    이 상태에서 스레드 D가 semWait()을 호출하면, 자원이 없는 상태이므로 스레드 D는 대기 상태로 전환됨

  6. 스레드 A가 작업을 마치고 semSignal()를 호출하면, 세마포어 값이 1로 증가하고, 대기 중인 스레드 중 하나( D )가 깨어나 자원에 접근함

  7. 스레드 D는 이제 자원을 사용하고, 다른 스레드가 semSignal()를 호출하기 전까지는 추가로 접근이 제한됨

  8. …. 이런 식으로 진행

세마포어는 사용할 수 있는 자원의 개수에 따라 두 가지 유형이 존재

  • 개수 세마포어 ( counting semaphore ) ⇒ 자원이 여러 개인 경우 사용 ( 자원의 수만큼 count 허용 )
  • 이진 세마포어 ( binary semaphore ) ⇒ 값이 0 또는 1만 가능 ( Mutex처럼 동작함 ) 뮤텍스와의 차이점은 lock을 해제할 수 있는 주체에 있음
profile
꾸준하게

0개의 댓글