본문 바로가기

CS/운영체제

[OS] 동기화 기법 뮤텍스 락, 세마포

728x90

동기화의 중요성: 레이스 컨디션과 임계 구역

공유 자원이란?

프로세스나 스레드가 공유하는 자원공유 자원(shared resource)이라고 합니다. 공유 자원은 메모리, 파일, 전역 변수, 입출력 장치 등 다양합니다.
여러 프로세스나 스레드가 동시에 동일한 공유 자원에 접근할 경우, 예기치 않은 문제가 발생할 수 있습니다.

 

 

레이스 컨디션 문제

다음은 두 개의 스레드가 count라는 공유 변수를 각각 100번씩 증가시키는 예제입니다.

public class RaceConditionExample {

    static int count = 0; // 공유 변수

    public static void main(String[] args) {
        Thread t1 = new Thread(new Counter());
        Thread t2 = new Thread(new Counter());

        // 두 스레드 실행
        t1.start();
        t2.start();

        // 메인 스레드가 t1, t2가 끝날 때까지 대기
        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Final count: " + count);
    }

    // 카운트를 증가시키는 작업 정의
    static class Counter implements Runnable {
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                count++;  // 카운트 증가
            }
        }
    }
}

이 코드를 실행하면 count가 200이 될 것 같지만, 실제로는 그렇지 않습니다. 왜 그럴까요?

 

컴퓨터에서의 실행 흐름

count++는 다음과 같은 세 단계로 분해됩니다.

mov eax, [count]   ; (1) Load: count 값을 eax 레지스터에 로드
add eax, 1         ; (2) Add: eax 레지스터의 값에 1을 더함
mov [count], eax   ; (3) Store: 수정된 값을 다시 count에 저장

 

문제는 두 스레드가 동시에 이 코드를 실행할 때 발생합니다.
각 스레드가 1단계에서 count 값을 가져온 후, 다른 스레드가 값을 수정하기 전에 덮어쓰는 경우가 발생합니다.
이런 상황을 레이스 컨디션(race condition)이라고 합니다.

 

임계 구역(critical section)

레이스 컨디션이 발생할 수 있는 코드 블록을 임계 구역(critical section)이라고 합니다.
임계 구역 내에서는 하나의 스레드만 자원에 접근해야 합니다. 그렇지 않으면 데이터 불일치가 발생합니다.

 

 

 

 

 

동기화(synchronization)의 필요성

레이스 컨디션 문제를 해결하려면 동기화가 필요합니다. 동기화는 두 가지 중요한 기능을 제공합니다.

  1. 실행 순서 제어: 여러 프로세스와 스레드가 올바른 순서로 실행되도록 보장
  2. 상호 배제(mutual exclusion): 하나의 스레드만 임계 구역에 접근할 수 있도록 제한

 

 

 

1. 뮤텍스 락 (Mutex Lock)

뮤텍스 락은 동시 접근이 허용되지 않아야 하는 자원에 대해 상호 배제(mutual exclusion)를 보장하는 동기화 기법입니다.
뮤텍스는 상호 배제를 의미하며, 뮤텍스 락은 자원에 접근하기 위해 획득해야 하는 키(lock)를 의미합니다.

 

뮤텍스 락의 원리

임계 구역에 접근하기 위해서는 반드시 락(lock)을 획득(acquire)해야 한다.
임계 구역에서의 작업이 끝났다면 반드시 락을 해제(release)해야 한다.

 

뮤텍스 락은 하나의 변수두 개의 함수로 구성됩니다.

  • 변수: lock (락의 상태를 저장)
  • 함수: acquire(), release()
lock.acquire();
// 임계 구역 작업
lock.release();

 

실행 과정 예시

공유 자원 1개에 대해 두 프로세스 P1과 P2가 접근하는 예시입니다.

  1. P1이 acquire() 호출 → 임계 구역에 진입
  2. P2가 acquire() 호출 → 락을 획득할 수 없으므로 대기
  3. P1이 작업을 마치고 release() 호출 → 락 해제
  4. P2가 락을 획득하고 임계 구역에 진입

 

의문점: 동시에 acquire() 호출 시 문제는 없을까?

동시에 두 스레드(또는 프로세스)가 acquire()를 호출하면, 레이스 컨디션이 발생할 것처럼 보일 수 있습니다. 그러나 이 문제는 CPU의 원자적(atomic) 명령어 덕분에 해결됩니다.

  • acquire() 메서드는 원자적 명령어로 실행됩니다.
  • 원자적 명령어는 동시에 여러 스레드가 메모리의 동일한 영역에 접근하지 못하도록 보장합니다.
  • 한 스레드가 락을 획득하는 동안, 다른 스레드는 대기 상태로 들어가며, 간섭 없이 락이 관리됩니다.

이 덕분에 레이스 컨디션이 방지되고, 데이터의 무결성(integrity)을 유지할 수 있습니다.

 

 

뮤텍스 락의 장단점

장점:

  • 임계 구역에 하나의 스레드만 접근할 수 있어 데이터 무결성 보장
  • 자원 공유에 대한 정확한 제어 제공

단점:

  • 락을 획득하려는 스레드가 많아지면 경합(contention)이 발생하여 성능 저하
  • 락을 해제하지 않는 경우 데드락(deadlock) 발생 가능

 

 

 

 

2. 세마포

세마포는 멀티스레딩 환경에서 여러 프로세스나 스레드가 공유 자원에 접근하는 순서를 제어하는 동기화 기법입니다. 주로 여러 스레드가 동시에 특정 자원에 접근할 때 발생하는 문제를 방지하기 위해 사용됩니다. 세마포는 공유 자원의 접근 횟수를 제한하여, 여러 개의 자원을 관리할 수 있는 장점이 있습니다.

세마포의 구조

세마포는 두 가지 중요한 함수인 wait() signal()을 기반으로 동작하며, 자원의 개수를 나타내는 변수 S로 자원에 접근 가능한 수를 나타냅니다.

  • S: 공유 자원의 개수
  • wait(): 자원에 접근할 때 호출하며, 자원이 있을 경우 S 값을 감소시키고 임계 구역에 진입합니다.
  • signal(): 자원 사용이 끝난 후 호출하며, 자원의 개수를 1 증가시킵니다.

세마포의 실행 과정

  1. wait() 호출:
    • S의 값을 1 감소시킴.
    • S가 0 이상이면, 프로세스나 스레드는 임계 구역에 진입할 수 있습니다.
    • S가 0 미만이면, 자원이 부족하여 프로세스가 대기 상태로 전환됩니다.
  2. 임계 구역에서 작업 수행:
    • 임계 구역에서 작업이 완료되면, signal()을 호출합니다.
  3. signal() 호출:
    • S의 값을 1 증가시킵니다.
    • 대기 중인 프로세스나 스레드가 있다면, 임계 구역에 진입할 수 있습니다.

 

 

이진 세마포와 카운팅 세마포

세마포는 이진 세마포(Binary Semaphore)카운팅 세마포(Counting Semaphore)로 나눌 수 있습니다. 이 두 종류는 자원 관리 방식과 적용되는 상황에 차이가 있습니다.

1. 이진 세마포 (Binary Semaphore)

이진 세마포는 뮤텍스(Mutex)와 유사하게 동작합니다. 즉, 하나의 자원만을 관리하며, 자원에 접근할 수 있는 최대 스레드 수가 1인 경우에 사용됩니다. S의 값은 0 또는 1만 가질 수 있습니다.

  • S = 1: 자원이 사용 가능함.
  • S = 0: 자원이 사용 중임.

특징

  • 하나의 자원만 제어하기 때문에, 한 번에 한 스레드만 임계 구역에 접근할 수 있습니다.
  • 프로세스 간 동기화에도 사용됩니다.

2. 카운팅 세마포 (Counting Semaphore)

카운팅 세마포는 여러 개의 공유 자원을 관리할 수 있습니다. S의 값은 0 이상의 값으로 설정되며, 여러 스레드가 동시에 자원에 접근할 수 있습니다. 자원의 개수를 지정하여, 특정 자원에 제한된 수의 스레드만 접근하도록 제어합니다.

  • S = N: N개의 자원이 사용 가능함.
  • S = 0: 자원이 모두 사용 중이며, 대기해야 함.

특징

  • 여러 자원을 관리할 수 있기 때문에, 특정 자원이 여러 개 존재하는 상황에 적합합니다.
  • 동시에 여러 스레드가 임계 구역에 접근할 수 있지만, 자원의 개수만큼만 접근을 허용합니다.

 

728x90