공유 자원
- 공유 자원은 시스템 내에서 프로세스, 스레드가 함께 접근할 수 있는 자원 및 변수 등을 의미함
- 공유 자원에 대해 동시에 읽거나 쓰는 상황을 경쟁 상태(race condition)이라고 함
- 경쟁 상태에 의한 주요 문제점
- 즉, 접근 타이밍 및 순서 등이 결과에 영향을 주는 상태

임계 영역
- 경쟁 상태에 의해 영향을 받는 영역을 임계 영역(critical section)이라고 함
- 임계 영역 해결 위한 세가지 방법
- 뮤텍스 (mutex)
- 세마포어 (semaphore)
- 모니터 (monitor)
- 위 세가지는 모두 상호 배제, 한정 대기, 융통성이라는 조건을 만족함
- 상호배제(mutex) - 한 프로세스가 임계 영역에 들어갔을 때 다른 프로세스는 들어갈 수 없음
- 한정대기 - 특정 프로세스가 영원히 임계 영역에 들어가지 못하는 것은 안 됨
- 융통성 - 한 프로세스가 다른 프로세스의 일을 방해하면 안 됨
- 해결 방법의 기본 메커니즘은 잠금(lock)을 활용하는 것
뮤텍스 (mutex)
- 동시 프로그래밍에서 공유 불가능한 자원의 동시 사용을 피하기 위해 사용하는 알고리즘
- 공유된 자원의 데이터 혹은 임계영역(Critical Section) 등에 하나의 Process 혹은 Thread가 접근하는 것을 막아줌 (동기화 대상이 하나)
- 다른 프로세스나 스레드 간 동기화를 위해 사용
- 공유 자원을 사용하기 전에 설정하고 사용한 후에 해제하는 잠금을 의미
- 잠금이 설정되면 다른 스레드는 잠긴 코드 영역에 접근할 수 없음
- 뮤텍스는 하나의 상태(잠금 or 해제)만 가짐
- 무겁고 느리다
상호배제를 보장하는 뮤텍스의 예시
package util.jsonparser;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MutexExample {
private static Lock mutex = new ReentrantLock();
public static void main(String[] args) {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
mutex.lock();
try {
System.out.println("Thread 1 acquired the mutex");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
mutex.unlock();
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
mutex.lock();
try {
System.out.println("Thread 2 acquired the mutex");
} finally {
mutex.unlock();
}
}
});
thread1.start();
thread2.start();
}
}
상호배제를 보장하기 위한 하드웨어적인 접근 방법 예시
****
const int n = ;
int bolt;
void P(int i)
{
while (true) {
while (compare_and_swap(&bolt, 0, 1) == 1)
;
;
bolt = 0;
;
}
}
void main()
{
bolt = 0;
parbegin (P(1), P(2), . . . ,P(n));
}
int compare_and_swap (int *word, int testval, int newval)
{
int oldval;
oldval = *word;
if (oldval == testval)
*word = newval;
return oldval;
}
****
int const n = ;
int bolt;
void P(int i)
{
while (true) {
int keyi = 1;
do exchange (&keyi, &bolt)
while (keyi != 0);
;
bolt = 0;
;
}
}
void main()
{
bolt = 0;
parbegin (P(1), P(2), . . ., P(n));
}
void exchange (int *register, int *memory)
{
int temp;
temp = *memory;
*memory = *register;
*register = temp;
}
세마포어 (semaphore)
- 멀티 프로그래밍 환경에서 공유된 자원에 대한 접근을 제한하는 방법
- 공유된 자원의 데이터 혹은 임계영역(Critical Section) 등에 여러 Process 혹은 Thread가 접근하는 것을 막아줌 (동기화 대상이 하나 이상)
- 세마포어는 일반화 된 뮤텍스이며, 원자적 함수로 조작되는 정수 변수
- 간단한 정수 값 + 두 가지 함수(wait, signal)로 공유 자원에 대한 접근 처리
- wait()은 자신의 차례 올 때까지 기다리는 함수
- signal()은 다음 프로세스로 순서를 넘겨주는 함수
- 프로세스가 공유 자원에 접근 시 세마포어에서 wait() 작업을 수행하고
- 프로세스가 공유 자원을 해제하면 세마포어에서 signal() 작업을 수행
- 세마포어는 조건 변수가 없고, 프로세스가 세마포어 값을 수정할 때 다른 프로세스는 동시에 세마포어 값을 수정할 수 없음
바이너리 세마포어
- 바이너리 세마포어는 0과 1의 두 가지 값만 가지는 세마포어
- 구현의 유사성으로 인해 뮤텍스는 바이너리 세마포어라고 할 수 있음 (두 가지 상태만 가지기 때문)
- 하지만 엄밀히 말하면, 뮤텍스는 리소스에 대한 접근을 동기화 하는데 사용되는 잠금이고,
- 세마포어는 신호를 기반으로 상호 배제가 일어나는 신호
typedef struct {
_Bool value;
queue waitQueue;
} binary_semaphore;
void semInitB(binary_semaphore s, int n)
{
s.value = n;
}
void semWaitB(binary_semaphore s)
{
if(s.value == 1)
s.value = 0;
else{
}
}
void semSignalB(binary_semaphore s)
{
if(s.waitQueue.empty())
s.value = 1;
else{
}
}
카운팅 세마포어 (범용 세마포어)
- 카운팅 세마포어는 여러 개의 값을 가질 수 있는 세마포어이며, 여러 자원에 대한 접근 제어할 때 사용
typedef struct{
int count;
queue waitQueue;
} semaphore;
void semInit(semaphore s, int n)
{
s.count = n;
}
void semWait(semaphore s)
{
s.count--;
if(s.count < 0){
}
}
void semSignal(semaphore s)
{
s.count++;
if(s.count <= 0){
}
}

세마포어를 이용한 Mutex 예시
semaphore s;
void thread_execute(int thread_no)
{
while(1){
semWait(s);
semSignal(s);
}
}
int main(void)
{
semInit(s, 1);
for(int i = 0; i < num_of_threads; i++)
thread_start(i);
}
import java.util.concurrent.Semaphore;
public class BinarySemaphoreExample {
private static Semaphore semaphore = new Semaphore(1);
public static void main(String[] args) {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
try {
semaphore.acquire();
System.out.println("Thread 1 acquired the semaphore");
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
try {
semaphore.acquire();
System.out.println("Thread 2 acquired the semaphore");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
semaphore.release();
}
}
});
thread1.start();
thread2.start();
}
}
모니터
- 모니터란 세마포어를 실제로 구현한 프로그램
- 모니터는 둘 이상의 스레드나 프로세스가 공유 자원에 안전하게 접근 가능하도록 공유 자원을 숨기고, 해당 접근에 대한 인터페이스만 제공
- 하나의 프로세스 내에서 다른 스레드 간 동기화 할 때 사용
- 프레임워크나 라이브러리 그 자체에서 제공됨
- 가볍고 빠름

- 모니터는 세마포어보다 구현하기 쉬우며 모니터에서 상호 배제는 자동인 반면, 세마포어에서는 상호 배제를 명시적으로 구현해야 하는 차이점이 존재
Mutex vs Mutual Exclusion
- Mutex는 Mutual Exclusion의 줄임 말로 임계 구역에 동시에 접근하지 못하도록 막는 기법을 의미
- 일반적으로 프로그래밍에서 Mutex를 사용하여 상호배제를 만족시킨다고 가정할 때 뮤텍스는 한 프로세스의 내부에서 여러 스레드의 임계구역 제어를 위해 사용하는 객체를 뜻한다.
예상 질문
- 경쟁 상태가 무엇인가요?
- 뮤텍스와 세마포어에 대해서 설명해주세요.
- 둘다 운영체제에서 동기화를 달성하기 위한 장치
- 세마포어는 뮤텍스가 될 수 있지만, 뮤텍스는 세마포어가 될 수 없음
- 세마포어는 소유할 수 없으며, 뮤텍스는 소유할 수 있고 소유주가 그에 대한 책임을 가짐
- 세마포어는 동기화 대상이 여러개 일 때 사용하고, 뮤텍스는 동기화 대상이 오로지 하나 일 때 사용
출처