Syncronization Algorithms

동기화 알고리즘은 병행 시스템에서 **상호 배제(Mutual Exclusion)**를 보장하기 위한 핵심 메커니즘이다.

데커 알고리즘 (Dekker’s Algorithm)

목적: 2개 프로세스의 상호 배제
메커니즘:

  • flag 배열(진입 의사) + turn 변수(진입 순서)
  • 교착 상태 방지를 위한 플래그 재설정과 턴 변경
    예시:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class DekkerLock:
    def __init__(self):
        # 각 프로세스의 진입 시도를 나타내는 플래그
        self.flag = [False, False]
        # 현재 임계 영역을 사용할 차례인 프로세스 번호
        self.turn = 0
    
    def lock(self, process_id):
        other = 1 - process_id  # 다른 프로세스의 ID
        
        # 진입 시도 표시
        self.flag[process_id] = True
        
        # 다른 프로세스가 진입 시도 중이면 대기
        while self.flag[other]:
            if self.turn != process_id:
                # 자신의 차례가 아니면 진입 시도 포기
                self.flag[process_id] = False
                # 자신의 차례가 될 때까지 대기
                while self.turn != process_id:
                    pass
                # 다시 진입 시도
                self.flag[process_id] = True
    
    def unlock(self, process_id):
        # 차례를 다른 프로세스에게 넘김
        self.turn = 1 - process_id
        # 진입 시도 표시 해제
        self.flag[process_id] = False

피터슨 알고리즘 (Peterson’s Algorithm)

목적: 2개 프로세스의 간단한 상호 배제
메커니즘:

  • flag 배열 + turn 변수 단일 관리
  • 데커 알고리즘보다 코드 복잡도 감소
    예시:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class PetersonLock:
    def __init__(self):
        # 각 프로세스의 진입 의도를 나타내는 플래그
        self.flag = [False, False]
        # 현재 차례
        self.turn = 0
    
    def lock(self, process_id):
        other = 1 - process_id  # 다른 프로세스의 ID
        
        # 진입 의도 표시
        self.flag[process_id] = True
        # 다른 프로세스에게 차례 양보
        self.turn = other
        
        # 다른 프로세스가 진입을 원하고
        # 현재 다른 프로세스의 차례이면 대기
        while self.flag[other] and self.turn == other:
            pass
    
    def unlock(self, process_id):
        # 진입 의도 해제
        self.flag[process_id] = False

램포트 빵집 알고리즘 (Lamport’s Bakery Algorithm)

목적: N개 프로세스의 공정한 상호 배제
메커니즘:

  • 번호표(number[]) + choosing[] 플래그
  • FIFO 공정성 보장
    예시:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class BakeryLock:
    def __init__(self, n):
        self.n = n  # 프로세스 수
        # 번호표 선택 중임을 나타내는 플래그
        self.choosing = [False] * n
        # 각 프로세스의 번호표 번호
        self.number = [0] * n
    
    def lock(self, process_id):
        # 번호표 선택 과정 시작
        self.choosing[process_id] = True
        # 가장 큰 번호표 값보다 1 큰 값 선택
        self.number[process_id] = max(self.number) + 1
        self.choosing[process_id] = False
        
        # 모든 다른 프로세스와 비교
        for j in range(self.n):
            # 다른 프로세스가 번호표 선택 중이면 대기
            while self.choosing[j]:
                pass
            # 우선순위 비교하여 대기
            while (self.number[j] != 0 and 
                   (self.number[j] < self.number[process_id] or 
                    (self.number[j] == self.number[process_id] and j < process_id))):
                pass
    
    def unlock(self, process_id):
        # 번호표 반납
        self.number[process_id] = 0

뮤텍스 (Mutex)

목적: 범용 상호 배제
메커니즘:

  • Lock/Unlock 기반의 하드웨어 지원 동기화
  • 대기 큐를 사용한 Blocking 방식
    예시:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from threading import Lock

class MutexExample:
    def __init__(self):
        # 뮤텍스 객체 생성
        self.mutex = Lock()
        # 공유 자원
        self.shared_resource = 0
    
    def critical_section(self):
        # 뮤텍스 잠금 획득
        self.mutex.acquire()
        try:
            # 임계 영역 작업 수행
            self.shared_resource += 1
            print(f"Current value: {self.shared_resource}")
        finally:
            # 뮤텍스 잠금 해제
            self.mutex.release()
    
    # context manager를 사용한 더 안전한 방식
    def better_critical_section(self):
        with self.mutex:
            self.shared_resource += 1
            print(f"Current value: {self.shared_resource}")

각 알고리즘의 예시

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 데커 알고리즘 사용 예시
dekker = DekkerLock()
def process_dekker(pid):
    for _ in range(5):
        dekker.lock(pid)
        # 임계 영역 작업
        dekker.unlock(pid)

# 빵집 알고리즘 사용 예시
bakery = BakeryLock(3)  # 3개 프로세스
def process_bakery(pid):
    for _ in range(5):
        bakery.lock(pid)
        # 임계 영역 작업
        bakery.unlock(pid)

# 피터슨 알고리즘 사용 예시
peterson = PetersonLock()
def process_peterson(pid):
    for _ in range(5):
        peterson.lock(pid)
        # 임계 영역 작업
        peterson.unlock(pid)

# 뮤텍스 사용 예시
mutex_example = MutexExample()
def process_mutex():
    for _ in range(5):
        mutex_example.better_critical_section()

종합 비교 분석

기준데커 알고리즘피터슨 알고리즘램포트 빵집 알고리즘뮤텍스
지원 프로세스 수2개2개N개N개
동기화 방식플래그 + 턴 변수플래그 + 턴 변수번호표 시스템Lock/Unlock
Busy Waiting사용 (CPU 자원 낭비)사용사용미사용 (대기 큐)
공정성턴 기반 순환턴 기반 순환FIFO(선착순)구현 방식에 따라 다름
확장성2개로 제한2개로 제한이론상 무제한높음
하드웨어 지원 필요NoNoNoYes (Atomic Operation)
현대 시스템 적용거의 없음 (교육용)거의 없음Linux ticket_spinlock광범위 (OS/DBMS 등)
주요 단점복잡한 조건 검사2개 프로세스 한정높은 메모리 오버헤드Context Switching 비용

바쁜 대기(Busy Waiting)
바쁜 대기는 프로세스나 스레드가 특정 조건이 만족될 때까지 계속해서 조건을 검사하면서 CPU 자원을 소비하며 대기하는 상태를 의미한다. 이는 마치 누군가가 문을 계속 두드리면서 열릴 때까지 확인하는 것과 유사하다.

적용 사례별 권장 알고리즘

  1. 2개 프로세스 간단 동기화: 피터슨 알고리즘
  2. N개 프로세스 공정한 접근: 램포트 빵집 알고리즘
  3. 실제 시스템 구현: 뮤텍스 또는 세마포어
  4. 교육/이론 연구: 데커 알고리즘

참고 및 출처