CS

동기화

kyoulho 2024. 8. 9. 20:20

Race Condition (경쟁 조건)

여러 프로세스나 스레드가 동시에 같은 데이터를 조작할 때 타이밍이나 접근 순서에 따라 결과가 달라질 수 있는 상황을 의미한다.

 

동기화 (Synchronization)

여러 프로세스나 스레드가 공유 자원에 접근하는 것을 제어하여 데이터 무결성을 유지하고 경쟁 조건을 방지하는 메커니즘이다. 동기화는 뮤텍스, 세마포어, 모니터 등을 사용하여 구현된다.

 

Critical Section (임계 영역)

공유 자원에 대해 동시에 하나의 프로세스나 스레드만 접근할 수 있는 코드 영역이다. 이를 통해 데이터의 무결성을 유지하고, 여러 스레드가 동시에 자원에 접근하여 발생할 수 있는 충돌을 방지할 수 있다.

 

Critical Section Problem (임계 영역 문제)

임계 영역에 대한 접근을 제어하여 데이터 일관성을 유지하기 위해 해결해야 하는 문제를 의미한다.

이 문제를 해결하기 위해서는 다음과 같은 네 가지 주요 요구사항을 충족해야 한다.

  1. Mutual Exclusion (상호 배제): 임계 영역에는 동시에 하나의 스레드만 접근할 수 있어야 한다. 이를 통해 공유 자원에 대한 충돌을 방지할 수 있다.
  2. Progress (진행): 임계 영역이 비어 있고, 임계 영역에 들어가려는 스레드가 대기 중이라면, 반드시 그중 하나가 임계 영역에 진입해야 한다.
  3. Bounded Waiting (한정된 대기): 특정 스레드가 임계 영역에 진입하기 위해 대기하는 최대 시간이 제한되어야 한다. 즉, 스레드가 임계 영역에 들어가기 위해 무한히 기다리지 않도록 보장해야 한다.
  4. No Assumptions (가정 금지): 하드웨어나 운영 체제의 특정 동작을 가정하지 않아야 한다. 이 요구사항은 다양한 시스템에서 일관되게 동작할 수 있도록 한다.

 

임계 영역 문제의 구조

do {
    entry section;
        critical section;
    exit section;
        remainder section;
} while (true);
  • Entry Section (진입 섹션): 임계 영역에 들어가기 전의 준비 단계이다. 이 단계에서 다른 스레드의 접근을 막기 위한 작업을 수행한다.
  • Critical Section (임계 영역): 공유 자원에 접근하거나 자원을 수정하는 코드가 포함된다. 동시에 하나의 스레드만 접근할 수 있어야 한다.
  • Exit Section (종료 섹션): 임계 영역을 나가기 전에 수행하는 작업이다. 다른 스레드가 임계 영역에 진입할 수 있도록 상태를 업데이트한다.
  • Remainder Section (나머지 섹션): 임계 영역 외의 작업을 수행하는 부분이다. 이 부분은 임계 영역과는 관계가 없으며, 임계 영역에 들어가기 전에 수행할 작업이다.

 

뮤텍스 (Mutex)

  • 정의: 뮤텍스는 "Mutual Exclusion"의 줄임말로, 상호 배제를 보장하는 동기화 도구이다. 단일 자원에 대한 동시 접근을 제어하는 데 사용된다.
  • 동작 방식:
    • 하나의 스레드가 뮤텍스를 소유하면, 다른 스레드는 해당 자원에 접근할 수 없다.
    • 뮤텍스를 소유한 스레드만이 해당 뮤텍스를 해제할 수 있다.
    • 대기 중인 스레드는 뮤텍스가 해제될 때까지 대기한다.
    • Java에서는 ReentrantLock과 같은 클래스가 뮤텍스 기능을 제공한다.
  • 특징:
    • 소유권 개념: 뮤텍스를 획득한 스레드만이 이를 해제할 수 있다.
    • 주로 운영 체제의 시스템 콜을 통해 구현된다.

 

조건 변수(Condition Variable)

동기화 및 스레드 간 통신을 지원하는 동기화 기법이다. 조건 변수는 스레드가 특정 조건을 만족할 때까지 대기하고, 조건이 만족되면 대기 중인 스레드를 깨우는 데 사용된다.

  • 조건 변수의 역할:
    • 조건 대기: 스레드가 특정 조건을 만족할 때까지 대기 상태로 전환할 수 있게 해 준다.
    • 조건 신호: 조건이 만족되었음을 알리기 위해 대기 중인 스레드를 깨우는 역할을 한다.
  • 기본 동작:
    • 대기 (wait()): 스레드는 조건 변수의 wait() 메서드를 호출하여 대기 상태로 전환된다. 이 메서드를 호출할 때, 스레드는 잠금을 해제하고 대기 큐에 들어간다.
    • 신호 (signal() 또는 notify()): 다른 스레드가 조건 변수의 signal() 또는 notify() 메서드를 호출하여 대기 중인 스레드 중 하나를 깨워서 대기 상태에서 벗어나게 한다.
    • 브로드캐스트 (broadcast() 또는 notifyAll()): 조건이 만족될 때 대기 중인 모든 스레드를 깨워서 대기 상태에서 벗어나게 한다.

 

스핀락 (Spinlock)

스핀락은 잠금을 획득하기 위해 대기하는 동안 CPU를 계속 소모하며, 잠금이 해제될 때까지 반복적으로 검사하는 동기화 기법이다.

  • 동작 방식:
    • 스레드는 잠금이 해제될 때까지 반복적으로 상태를 체크한다.
    • 잠금이 자주 해제되면 스핀락이 효율적일 수 있다.
  • 특징:
    • CPU 자원을 계속 소모하며, 잠금 대기 시간이 길어질 경우 성능 저하를 초래할 수 있다.
    • 싱글 코어 환경에서는 비효율적입니다. 멀티 코어 환경에서 효과적일 수 있다.

 

세마포어 (Semaphore)

세마포어는 자원에 대한 접근을 제어하는 신호 메커니즘을 사용하는 동기화 기법이다. 특정 자원의 수를 관리하고, 자원 접근을 조절한다.

  • 동작 방식:
    • 이진 세마포어 (Binary Semaphore): 값이 0 또는 1만 가질 수 있으며, 뮤텍스와 유사하게 동작한다. 동시에 하나의 스레드만 임계 영역에 접근할 수 있다.
    • 카운팅 세마포어 (Counting Semaphore): 0 이상의 값을 가질 수 있으며, 여러 스레드가 동시에 자원에 접근할 수 있도록 허용합니다. 자원의 개수에 따라 동시 접근 가능한 스레드 수를 조절한다.
  • 특징:
    • 소유권 개념이 없으며, 신호를 보내는 스레드와 대기하는 스레드가 다를 수 있다.
    • 자바에서는 java.util.concurrent.Semaphore 클래스를 통해 구현된다.

 

모니터 (Monitor)

모니터는 상호 배제를 보장하고, 특정 조건에 따라 스레드가 대기 상태로 전환될 수 있는 동기화 기법이다. 스레드 간의 협업과 통신을 위한 구조체이다.

  • 동작 방식:
    • 모니터는 동시에 하나의 스레드만 임계 영역에 접근할 수 있도록 보장한다.
    • 조건 변수와 함께 사용하여 특정 조건이 만족될 때까지 스레드를 대기 상태로 전환할 수 있다.
  • 특징:
    • 뮤텍스 (Mutex): 모니터의 임계 영역에서 상호 배제를 보장한다.
    • 조건 변수 (Condition Variables): 스레드가 특정 조건을 만족할 때까지 대기하게 하고, 조건이 만족되면 대기 중인 스레드를 깨운다. 주요 메서드로 wait(), notify(), notifyAll()이 있다.
    • 자바에서는 synchronized 블록과 Object.wait(), Object.notify()를 통해 모니터를 구현한다. 또한, java.util.concurrent.locks.Condition을 사용할 수도 있다.

 

우선순위 상속 (Priority Inheritance)

스레드 동기화에서 교착 상태(deadlock)와 우선순위 역전(priority inversion)을 방지하기 위해 사용되는 기술이다.

 

우선순위 역전 (Priority Inversion)

우선순위 역전은 높은 우선순위를 가진 스레드가 낮은 우선순위를 가진 스레드에 의해 블로킹되는 상황을 말한다.

예를 들어, 높은 우선순위의 스레드가 뮤텍스를 필요로 할 때, 낮은 우선순위의 스레드가 그 뮤텍스를 소유하고 있는 경우, 이 낮은 우선순위의 스레드가 그 뮤텍스를 해제하기 전까지 높은 우선순위의 스레드는 실행되지 못한다. 이 경우, 낮은 우선순위의 스레드가 높은 우선순위의 스레드를 블로킹하므로 우선순위 역전이 발생한다.

 

우선순위 상속의 동작

우선순위 상속 기술은 이러한 우선순위 역전을 방지하기 위해, 뮤텍스를 소유한 낮은 우선순위 스레드의 우선순위를 일시적으로 높여주는 방식으로 동작한다.

  1. 높은 우선순위 스레드가 뮤텍스를 요청: 높은 우선순위 스레드가 뮤텍스를 필요로 할 때, 해당 뮤텍스를 소유한 낮은 우선순위 스레드가 블로킹된다.
  2. 우선순위 상속: 뮤텍스를 소유한 낮은 우선순위 스레드는 높은 우선순위 스레드의 우선순위를 일시적으로 상속받는다. 이를 통해 낮은 우선순위 스레드는 더 빨리 작업을 완료하고 뮤텍스를 해제할 수 있다.
  3. 뮤텍스 해제: 낮은 우선순위 스레드가 뮤텍스를 해제하면, 높은 우선순위 스레드가 작업을 계속 진행할 수 있다.
  4. 우선순위 복원: 낮은 우선순위 스레드는 원래의 우선순위로 복원된다.

 

우선순위 상속을 지원하는 뮤텍스

  • 뮤텍스: 우선순위 상속 기능을 지원하는 뮤텍스는 이러한 우선순위 상속을 자동으로 처리한다. 대부분의 운영 체제와 실시간 커널에서는 뮤텍스를 사용하여 이 기능을 지원한다.
  • 임계 구역: 일반적인 임계 구역의 구현에서는 우선순위 상속을 고려하지 않으므로, 이 기능을 제공하는 특정 라이브러리나 운영 체제의 뮤텍스를 사용해야 한다.

 

자바에서의 우선순위 상속

자바의 java.util.concurrent 패키지에서 제공하는 동기화 도구(예: ReentrantLock)는 우선순위 상속을 기본적으로 지원하지 않는다. 하지만, 특정 운영 체제나 라이브러리에서 제공하는 뮤텍스와 같은 동기화 도구는 우선순위 상속을 지원할 수 있다.

우선순위 상속은 특히 실시간 시스템에서 중요한 역할을 하며, 높은 우선순위의 작업이 가능한 한 빠르게 처리되도록 보장한다.

'CS' 카테고리의 다른 글

프로세스/스레드 상태 변화  (0) 2024.08.11
Deadlock  (0) 2024.08.11
CPU Bound, I/O Bound  (0) 2024.08.09
컨텍스트 스위칭  (0) 2024.08.09
프로세스와 스레드  (1) 2024.08.09