적당한 고통은 희열이다

- 댄 브라운 '다빈치 코드' 중에서

컴퓨터시스템

동기화를 위한 방법들 Spinlock / Mutex / Semaphore

hongssup_ 2022. 9. 5. 18:33
반응형

참고 : youtube - 쉬운코드 를 참고하여 정리한 것임 

 

동기화 매커니즘 세 가지

- 스핀락 Spinlock

- 뮤텍스 Mutex

- 세마포 Semaphore

 

race condition 경쟁 조건

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

synchronization 동기화

: 여러 프로세스/스레드를 동시에 실행해도 공유 데이터의 일관성을 유지하는 것

critical section 임계 영역

: 공유 데이터의 일관성을 보장하기 위해 하나의 프로세스/스레드만 진입해서 실행(mutual exclusion) 가능한 영역

 

Spinlock

mutual exclusion 을 위해 lock 을 가질 수 있을 때까지 반복해서 시도하는 방법

lock 을 계속 확인하면서 기다리는 동안 CPU를 낭비한다는 단점..

// 예시)
volatile int lock = 0; // global

void critical() {
    while (test_and_set(&lock) == 1);
    ... critical section
    lock = 0;
}

int TestAndSet(int* lockPtr) {
    int oldLock = *lockPtr;
    *lockPtr = 1;
    return oldLock;
}

위의 예시는 lock 선언 후 while loop 내에서 lock을 획득한 스레드(T1)가 critical section에 들어와 실행 후 최종적으로 lock을 반환하는 형태로 동작. 다음 스레드(T2)는 이전 스레드(T1)가 끝날 때까지 while loop 내에서 계속 돌게됨 (while 1 == 1 무한루프). T1에서 lock을 다시 초기화해주면 T2도 while문을 탈출해 critical section 에서 작업 완료 후 lock을 반환하고 함수를 종료 

TestAndSet은 CPU atomic 명령어로 다음과 같은 특징을 가진다.

- 실행 중간에 간섭받거나 중단되지 않는다

- 같은 메모리 영역에 대해 동시에 실행되지 않는다. 

=> atomic하기 때문에 CPU 레벨에서 알아서 동기화 시켜줘서, test_and_set에서 context switching 일어나 다른 스레드가 실행될 일은 없음

 

Mutex

여러 스레드를 실행하는 환경에서 자원에 대한 접근에 제한을 강제하기 위한 동기화 매커니즘 (Mutual Exclusion 상호 배제)

- lock 이 준비될 때까지 스레드를 blocking 하고 대기큐에 넣어 sleep 시키는 방법 

  CPU 낭비를 최소화 하기 위해 lock() 으로 스레드를 큐에 넣고, unlock() 을 통해 큐에 대기중인 스레드가 있으면 깨우는 방식

- Spinlock과 마찬가지로 atomic한 명령어 사용

value 값을 통해 critical section 영역에 안전하게 들어갈 수 있도록 guard 값도 필요.  

 

Spinlock vs. Mutex
싱글 코어 환경에서는 스핀락의 이점 거의 없음.
멀티 코어 환경일 때, critical section 에서의 작업이 context switching 보다 더 빨리 끝난다면 스핀락이 뮤텍스보다 더 효율적일 수도 있음.

 

Semaphore

멀티프로그래밍 환경에서 다수의 프로세스/스레드의 여러 개의 공유 자원에 대한 접근을 제한하는 동기화 기법

signal machanism 을 가진, 하나 이상의 프로세스/스레드가 critical section 에 접근 가능하도록 하는 장치

- wait() 와 signal() 을 이용하여 작업의 순서를 정해줄 수 있음.

- signal() 과 wait() 가 같은 스레드에서 실행될 필요는 없다. 

- value 값으로 다양한 값을 가질 수 있으며 

  value 값이 1인 세마포 (= binary semaphore 이진 세마포) : 하나의 스레드만 critical section 접근 가능하도록. 

  value > 1 인 경우 counting semaphore. 

 

Mutex vs. Semaphore
뮤텍스는 락을 가진 스레드/프로세스만 락을 해제할 수 있지만 세마포는 어느 스레드에서 signal()을 날리고 어디서 wait()를 날릴 지 알 수 없다. (세마포는 여러 스레드가 동시에 접근할 수 있다는 차이)
→ 상호 배제(mutual exclusion)만 필요하다면 뮤텍스를, 작업 간의 실행 순서 동기화가 필요하다면 세마포를 권장

 

Mutex - 여러 스레드를 사용하는 환경에서 자원에 대한 접근을 강제하기 위한 동기화 매커니즘
- Boolean 타입의 Lock 변수를 사용
- 공유자원을 사용중인 스레드가 있을 때, 다른 스레드가 공유자원에 접근한다면 Blocking 후 대기 큐로 보낸다. 
- Lock을 건 스레드만 Lock을 해제할 수 있다. 
Spinlock - 기본적으로 Mutex 와 유사
- Busy-waiting 하며 대기 큐를 갖지 않는다
Semaphore - semaphore 변수를 통해 wait, signal을 관리한다. semaphore 변수는 0 이상의 정수형 변수를 갖는다. 
- 접근 가능한 공유 자원의 수가 1개일 때는 이진 세마포어로 뮤텍스처럼 사용할 수 있다. 
- lock() 를 걸지 않은 스레드도 signal()을 보내 해제할 수 있다. 

ex) 식당 줄서기. 

자리 하나 뿐인 식당

- 스핀락 : 문앞에서 자리 있냐고 계속 물어보기

- 뮤텍스 : 대기실에 이동했다가 자리 나면 들어오기

자리 n개인 식당

- 세마포어 : 식당 들어오면 wait() 나갈 때 signal() 날리기. n개 이상으로 사람 오면 위 두가지 방법 중 하나로 기다리기. 

728x90
반응형