Backend study/Backend theory
뮤텍스(Mutex)와 세마포어(Semaphore)
adulty22
2024. 9. 24. 23:07
뮤텍스(Mutex)와 세마포어(Semaphore)는 동시성 제어 기법으로, 여러 프로세스나 스레드가 공유 자원에 접근할 때 발생하는 경쟁 조건(race condition)을 방지하기 위해 사용된다. 이 두 개념은 동기화 문제를 해결하는 데 중요한 역할을 하며, 각기 다른 특성과 용도로 사용된다.
1. 뮤텍스(Mutex)
뮤텍스란?
- Mutex는 Mutual Exclusion의 약자로, 상호 배제를 의미한다. 하나의 스레드나 프로세스만이 특정 시점에 공유 자원에 접근할 수 있도록 잠금(Lock)을 제공하는 동기화 도구이다.
- 단일 사용자: 특정 공유 자원에 대해 동시에 하나의 스레드만 접근할 수 있다. 다른 스레드가 동일한 자원에 접근하려면, 먼저 사용 중인 스레드가 자원의 잠금을 해제해야 한다.
뮤텍스의 동작 원리
- 잠금(Lock): 자원에 접근하기 전에 해당 자원을 잠근다. 잠금이 걸리면 다른 스레드는 그 자원에 접근하지 못하고 대기 상태가 된다.
- 잠금 해제(Unlock): 자원의 사용이 끝나면 잠금을 해제하여 다른 스레드가 그 자원에 접근할 수 있게 한다.
- 상호 배제: 한 스레드가 자원을 사용하는 동안 다른 스레드가 동시에 접근하는 것을 방지함으로써, 데이터 불일치나 데이터 손상을 방지한다.
뮤텍스의 특징
- 소유권: 뮤텍스는 잠금을 설정한 스레드만 잠금을 해제할 수 있다. 즉, 잠금을 설정한 스레드만 자원의 사용을 끝내고 잠금을 해제할 수 있다.
- 단일 잠금: 뮤텍스는 동시에 하나의 스레드만 자원을 사용할 수 있다. 여러 스레드가 동시에 접근하는 것은 불가능하다.
- 경량: 뮤텍스는 일반적으로 빠르고 간단한 동기화 기법으로, 하나의 자원을 보호할 때 자주 사용된다.
뮤텍스 사용 예시 (Python)
import threading
# 공유 자원
counter = 0
# 뮤텍스 객체 생성
lock = threading.Lock()
def increment():
global counter
for _ in range(1000000):
# 뮤텍스 잠금
with lock:
counter += 1
# 두 개의 스레드를 생성하여 동시에 실행
thread1 = threading.Thread(target=increment)
thread2 = threading.Thread(target=increment)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print(f"Final counter value: {counter}")
위 코드에서 lock 객체는 뮤텍스로 동작한다. 각 스레드는 with lock을 통해 자원에 잠금을 설정한 후 공유 자원에 접근한다. 이로 인해 두 스레드가 동시에 counter를 증가시키는 작업을 할 때 동기화 문제가 발생하지 않는다.
2. 세마포어(Semaphore)
세마포어란?
- 세마포어(Semaphore)는 동기화 도구로, 정해진 수의 스레드나 프로세스만 동시에 특정 자원에 접근할 수 있게 제한하는 방식이다. 세마포어는 계수기(Counter)를 사용하여 자원에 접근할 수 있는 스레드 또는 프로세스의 최대 수를 제한한다.
- 멀티 사용자 허용: 세마포어는 여러 개의 스레드가 동시에 자원에 접근할 수 있도록 허용한다. 예를 들어, 세마포어의 값이 3이라면, 최대 3개의 스레드가 동시에 자원에 접근할 수 있다.
세마포어의 동작 원리
- 카운터: 세마포어는 내부적으로 카운터를 유지한다. 카운터의 값은 자원에 접근할 수 있는 스레드의 수를 나타낸다.
- 세마포어의 초기 값은 자원에 접근할 수 있는 최대 스레드 수로 설정된다.
- P(S) 연산(Wait/Acquire): 세마포어의 값을 감소시키는 연산이다. 세마포어의 값이 0보다 클 경우, 스레드는 자원에 접근할 수 있으며 세마포어 값을 감소시킨다. 만약 세마포어 값이 0이면, 스레드는 자원이 사용 가능할 때까지 대기한다.
- V(S) 연산(Signal/Release): 세마포어의 값을 증가시키는 연산이다. 자원을 다 사용한 스레드는 세마포어 값을 증가시켜, 다른 스레드가 자원에 접근할 수 있도록 한다.
세마포어의 특징
- 카운팅 세마포어: 세마포어의 값이 1 이상일 경우, 여러 스레드가 동시에 자원에 접근할 수 있다. 예를 들어, 값이 3이면 최대 3개의 스레드가 동시에 접근할 수 있다.
- 바이너리 세마포어: 세마포어 값이 0 또는 1로만 설정되는 경우는 바이너리 세마포어라고 하며, 이 경우 뮤텍스처럼 동작한다. 단, 소유권 개념이 없으므로 잠금을 설정한 스레드 외에도 잠금을 해제할 수 있다.
- 소유권 없음: 세마포어는 자원에 대한 소유권을 가지지 않기 때문에, 잠금을 설정한 스레드가 아닌 다른 스레드도 자원 해제를 할 수 있다.
세마포어 사용 예시 (Python)
import threading
import time
# 세마포어 객체 생성 (최대 3개의 스레드가 동시에 접근 가능)
semaphore = threading.Semaphore(3)
def task(n):
# 세마포어 획득
semaphore.acquire()
print(f"Thread {n} is working...")
time.sleep(2) # 작업 수행
print(f"Thread {n} is done.")
# 세마포어 해제
semaphore.release()
# 다수의 스레드 생성
threads = []
for i in range(6):
t = threading.Thread(target=task, args=(i,))
threads.append(t)
t.start()
for t in threads:
t.join()
위 예시에서 세마포어는 최대 3개의 스레드만 동시에 task 함수에 접근할 수 있게 제한한다. 총 6개의 스레드가 생성되지만, 한 번에 3개의 스레드만 작업을 수행하고, 나머지 스레드는 대기 상태로 있다가 이전 작업이 끝나면 세마포어에 의해 자원 접근이 허용된다.
3. 뮤텍스와 세마포어의 비교
특징 | 뮤텍스(Mutex) | 세마포어(Semaphore) |
동작 방식 | 자원에 대한 단일 스레드의 잠금을 보장 | 여러 스레드가 동시에 자원에 접근할 수 있음 |
잠금 상태 | 자원이 잠겨 있으면 다른 스레드는 대기 | 세마포어 값이 0이 될 때까지 대기 |
소유권 | 잠금한 스레드만 잠금을 해제할 수 있음 | 소유권 개념이 없으며, 누구든지 해제 가능 |
동시 접근 가능성 | 한 번에 하나의 스레드만 접근 가능 | 여러 개의 스레드가 동시에 접근 가능 |
사용 사례 | 단일 자원 보호: 예를 들어 공유 변수나 파일 | 제한된 자원 보호: 예를 들어 데이터베이스 연결 |
카운터 | 카운터 없음, 단일 잠금 | 카운터로 접근 가능한 스레드 수 제어 |
4. 뮤텍스와 세마포어의 사용 사례
뮤텍스 사용 사례
- 공유 변수 보호: 스레드가 동시에 공유 변수에 접근할 때, 뮤텍스를 사용하여 단일 스레드만 해당 변수를 읽고 수정할 수 있도록 보호한다.
- 파일 입출력: 여러 스레드가 동시에 파일에 접근해 읽거나 쓸 때, 뮤텍스를 사용해 파일의 일관성을 유지한다.
세마포어 사용 사례
- 데이터베이스 연결 제한: 데이터베이스 연결의 최대 수를 제한할 때, 세마포어를 사용해 여러 클라이언트가 동시에 제한된 수의 연결에만 접근할 수 있도록 제어한다.
- 제한된 리소스: 예를 들어, 네트워크 포트나 프린터처럼 동시 접근할 수 있는 수가 제한된 자원에 접근할 때, 세마포어를 사용하여 허용 가능한 최대 접근 수를 제어한다.
- 뮤텍스(Mutex)는 단일 스레드만 자원에 접근하도록 제어하는 동기화 도구이다. 주로 단일 자원에 대한 상호 배제를 보장하여 동기화 문제를 해결하는 데 사용된다.
- 세마포어(Semaphore)는 여러 스레드가 동시에 자원에 접근할 수 있도록 하며, 자원에 대한 동시 접근 가능 스레드 수를 제어하는 데 사용된다. 카운팅 세마포어는 여러 개의 스레드가 동시에 자원을 사용할 수 있지만, 제한된 자원을 보호하는 데 유용하다.
이 두 동기화 기법은 프로세스와 스레드가 동시에 접근할 수 있는 공유 자원을 안전하게 보호하고, 경쟁 상태를 방지하는 데 필수적이다.
728x90