발생 가능성:
주요 사례:
두 스레드가 동시에 공유 데이터를 읽을 경우
한 스레드는 데이터를 쓰고, 다른 스레드는 데이터를 읽을 경우
두 스레드가 동시에 데이터를 쓰는 경우
예시:
정의:
공유 데이터의 일관성을 유지하기 위해 다수의 스레드가 접근할 때 특정 규칙을 적용하는 기법.
배타적 독점(Mutual Exclusion)을 통해 데이터 접근을 순서대로 처리.
목적:

상황: 학생들이 집계판에 10씩 더하는 작업을 수행.
동시 접근 문제:
학생 A와 학생 B가 동시에 방에 들어와 집계판의 데이터를 수정.
두 학생이 50에서 시작하여 각각 60으로 계산.
결과적으로 집계판에는 잘못된 값(60)이 기록됨.
이슈:
경쟁 상태(Race Condition) 발생.
두 스레드(학생)가 동시에 데이터를 수정하면서, 이전 작업 결과가 손실.
배타적 접근(Mutual Exclusion):
정상 동작:

참여자:
sum계산식:
sum = sum + 10;
sum 값에 10을 더한 결과를 다시 sum에 저장.공유 변수 접근 과정:
각 스레드(T1, T2)는 다음 단계를 독립적으로 실행:
sum 값을 읽어 레지스터에 저장.sum에 저장.문제 발생:
sum을 읽는 경우, 동일한 값을 기반으로 계산.동작 과정:
mov ax, sum : 공유 변수 sum을 ax 레지스터로 복사.add ax, 10 : ax 값에 10을 더함.mov sum, ax : 계산된 값을 다시 sum에 저장.문제:
mov ax, sum을 실행하면, 동일한 초기 값을 읽게 됨.sum 값이 예상과 다르게 기록됨.sum에 동시에 접근할 때 발생하는 문제
T1과 T2 스레드는 공유 변수 sum에 각각 10을 더하는 작업을 수행.
두 스레드가 동시에 sum을 수정하려 할 때, 결과가 의도한 값(70)이 아닌 잘못된 값(60)으로 기록될 수 있음.
T1이 sum 값을 읽어 레지스터(ax)에 저장.
sum = 50.ax = 50으로 설정.T1의 실행이 타이머 인터럽트로 중단되고, CPU는 T1의 상태를 TCB1(Task Control Block 1)에 저장.
sum을 읽고 60으로 계산한 뒤, 결과를 sum에 저장.sum = 60.T1이 다시 실행되고, 이전에 저장된 상태(TCB1)를 복원.
레지스터에 남아 있던 값 ax = 50을 기반으로 sum = 60으로 다시 계산하고 저장.
sum 값이 60으로 덮어씌워짐.의도한 결과:
실제 결과:
공유 자원 동시 접근:
sum 값을 읽고 수정 작업을 수행.타이머 인터럽트:
여러 스레드가 공유 변수에 접근할 때 데이터 무결성이 훼손될 수 있음.
핵심 아이디어:
매우 자주 발생:
사용자 프로그램:
커널 코드:
다중 코어 환경:
정의: 공유 데이터에 접근하는 코드 블록.
목적: 공유 데이터를 보호하고 데이터의 무결성을 보장.
예시: sum = sum + 10;와 같은 코드가 공유 데이터에 접근하는 경우.
T1과 T2의 임계구역 접근:
T1: 임계구역(sum = sum + 10;) 실행 중.
T2: T1이 작업을 마칠 때까지 대기 → 상호배제 성공.
은행 시스템: 계좌 잔액을 업데이트할 때 발생할 수 있는 동시성 문제 해결.
파일 시스템: 여러 스레드가 동일한 파일에 쓰기를 시도하는 경우.
정의: 공유 데이터를 액세스하지 않는 코드 블록.
특징:
정의: 임계구역에 들어가기 전 필요한 코드 블록.
기능:
정의: 공유 데이터에 접근하거나 조작하는 코드 블록.
중요성:
정의: 임계구역을 나갈 때 실행되는 코드 블록.
역할:

일반 코드 실행.
임계구역 진입 코드 실행 → 임계구역 진입 여부 확인.
임계구역 코드 실행 → 공유 데이터 접근.
임계구역 진출 코드 실행 → 진입 잠금 해제.
다시 일반 코드로 돌아감.
임계구역에 오직 한 개의 스레드만 진입하도록 보장.
소프트웨어적 방법
하드웨어적 방법
인터럽트 서비스 금지
원자 명령 (Atomic Instruction) 사용
CPU 명령으로 상호 배제를 보장.
원자 명령 예:
인터럽트 서비스 금지 방법은 특정 임계구역 코드 실행 중 인터럽트를 비활성화하여, 다른 스레드가 CPU를 점유하지 못하도록 함.
cli - clear interrupt flag).sti - set interrupt flag).임계구역 진입 시:
cli 명령어를 통해 인터럽트를 비활성화.임계구역 종료 시:
sti 명령어를 통해 인터럽트를 다시 활성화.모든 인터럽트가 무시됨:
멀티 코어 환경에서 비효율적:
cli가 다른 코어에는 영향을 미치지 않음.
문제 발생 과정:
T1이 임계구역 실행 중: T1 스레드가 임계구역의 코드를 실행.
인터럽트 발생: T1이 실행 중 인터럽트가 발생하여 CPU가 인터럽트 서비스 루틴으로 전환.
T2 스케줄링: T2 스레드가 임계구역에 진입.
임계구역 충돌: T1과 T2가 동시에 임계구역 코드를 실행하게 되어 데이터 충돌 발생.
문제 해결 과정:
T1이 임계구역 실행 중: T1 스레드가 cli 명령어를 통해 인터럽트를 금지하고 임계구역 코드를 실행.
인터럽트 발생 무시: CPU는 인터럽트를 무시하며 T1이 임계구역 실행을 완료할 때까지 진행.
임계구역 종료: T1이 sti 명령어로 인터럽트를 허용하며 임계구역 실행 종료.
T2 스케줄링: 이후 T2가 스케줄링되어 임계구역에 진입.
인터럽트를 금지하지 않은 경우:
인터럽트를 금지한 경우:

목표
Lock 변수의 초기값: 0 (열린 상태)
Entry 코드 (Locking):
Exit 코드 (Unlocking):
l1: mov ax, lock ; lock 변수를 읽음
mov lock, 1 ; lock 변수에 1 저장
cmp ax, 0 ; 이전 lock 값이 0인지 비교
jne l1 ; 0이 아니면 다시 점검 (대기)
임계구역 코드:
Exit 코드:
mov lock, 0 ; lock 변수를 0으로 해제
경쟁 상태 (Race Condition):
Lock 값을 읽고 쓰는 과정이 두 스레드에서 겹치면 동시 접근 문제 발생 가능.
예: T1과 T2가 동시에 mov ax, lock을 실행하면, 둘 다 0으로 판단하여 임계구역에 동시 진입.
단순 Lock 변수로 상호배제를 구현하면 경쟁 상태를 해결하지 못함.
해결책:

T1의 임계구역 진입:
T1이 mov ax, lock을 실행해 lock 값을 읽음.
lock 값이 0이므로 mov lock, 1을 실행하여 lock 값을 1로 설정.
cmp ax, 0에서 lock이 0이 아니므로 임계구역에 진입.
T1 실행 중단 및 T2 대기:
T1이 임계구역을 실행하는 도중 CPU 스케줄러가 T2로 스위칭.
T2가 mov ax, lock으로 lock 값을 읽음. lock 값은 1.
T2는 lock 값이 0이 될 때까지 jne l1로 대기.
T1 임계구역 종료 및 lock 해제:
mov lock, 0).T2의 임계구역 진입:
T2가 lock 값이 0이 된 것을 감지하고 mov lock, 1로 설정.
T2는 임계구역에 진입하여 실행.
성공적인 상호배제:
T1과 T2는 lock 변수의 상태를 바탕으로 번갈아 가며 임계구역에 진입.
lock 값이 0에서 1로 변경되고, 다른 스레드는 lock 값이 0이 될 때까지 대기.
장점:
단순한 구조로 임계구역 접근을 제어 가능.
한 스레드가 임계구역을 완료할 때까지 다른 스레드는 대기.
제한점:
lock 변수 조작 과정에서 원자성 보장이 필요.
경쟁 상태(race condition)가 발생할 경우 제대로 작동하지 않을 가능성.

T1 시작 및 Lock 변수 읽기:
T1이 mov ax, lock으로 lock 값을 읽음.
lock 값은 0이며, T1은 lock 값을 1로 변경하려고 준비.
T1 중단 및 T2 시작:
T1이 lock 값을 1로 설정하기 전에 CPU 컨텍스트가 T2로 스위칭.
T2가 mov ax, lock을 실행하여 lock 값(0)을 읽음.
T2는 lock 값을 1로 설정하고 임계구역에 진입.
T2 중단 및 T1 재개:
T2가 lock 값을 1로 설정한 상태에서 CPU가 다시 T1으로 스위칭.
T1은 이전에 읽어둔 lock 값(0)을 기반으로 lock 값을 1로 변경.
결과적으로 T1과 T2가 동시에 임계구역에 진입하는 충돌 상황 발생.
Race Condition:
T1과 T2가 lock 값을 동시에 읽고, lock 값을 설정하는 과정에서 경쟁이 발생.
Lock 변수 조작이 원자적이지 않아서 두 스레드가 동일한 lock 상태를 기준으로 동작.
Non-Atomic Operation:
mov ax, lock → mov lock, 1 → cmp ax, 0 이 3단계로 나뉘어 실행.
각 단계 사이에 컨텍스트 스위칭이 발생하면서 데이터 불일치 문제가 발생.
컨텍스트 스위칭 발생:
두 명령어 (mov ax, lock → mov lock, 1) 사이에 컨텍스트 스위칭이 발생.
Lock 변수 값을 읽은 상태에서 다른 스레드로 전환되면서 상호배제가 실패.

명령어 실행 순서 문제:
Lock 값을 읽고 수정하는 작업이 여러 단계로 나뉘어 처리됨.
이로 인해 명령어가 완료되기 전에 다른 스레드가 임계구역에 진입할 수 있음.
원자적 실행 보장:
여러 단계의 명령을 하나의 연산으로 처리.
컨텍스트 스위칭이 중간에 발생하지 않도록 보장.
TSL(Test and Set Lock) 명령어:
원자적으로 lock 값을 읽고 수정.
TSL ax, lock:
Lock 값(현재 상태)을 읽어 ax 레지스터에 저장.
동시에 lock 값을 1로 변경하여 다른 스레드의 접근을 방지.
T1 실행:
TSL ax, lock 실행.ax에 저장하고, lock 값을 1로 설정.T2 대기:
TSL 실행을 시도하지만, lock 값이 이미 1로 설정되어 있음.T1 종료 및 Lock 해제:

구성
mov ax, lock: Lock 값을 읽음.
mov lock, 1: Lock 값을 1로 설정.
cmp ax, 0: Lock 값이 0인지 비교.
jne l1: Lock 값이 0이 아니면 점프.
문제
두 명령 (mov ax, lock → mov lock, 1) 사이에 컨텍스트 스위칭이 발생할 가능성.
이로 인해 두 스레드가 동시에 임계구역에 진입하는 상황(상호배제 실패) 발생.
구성
TSL ax, lock:
ax에 저장.cmp ax, 0: 이전 Lock 값이 0인지 확인.
jne l1: Lock 값이 0이 아니면 점프.
동작 원리
mov lock, 0:
정의: 상호배제(Mutual Exclusion) 기반에서, 여러 스레드가 자원을 원활히 공유하도록 하는 기법.
핵심 요소: 동기화 프리미티브(Synchronization Primitives)를 활용.
Locks 방식
뮤텍스(Mutex):
스핀락(Spinlock):
Wait-Signal 방식
| 방식 | 사용 사례 | 특징 |
|---|---|---|
| 뮤텍스 | 단일 자원 보호 | 상호배제 보장, 단순한 구현 |
| 스핀락 | Lock 대기 시간이 짧은 경우 | CPU 자원 소모, 짧은 대기 적합 |
| 세마포 | 다중 자원 관리 | 여러 스레드에서 자원 할당 가능 |
목적: 한 스레드만 임계구역에 진입할 수 있도록 보장.
대기 방식: 다른 스레드는 큐(queue)에 대기.
특징: Sleep-Waiting Lock 기법 사용.
락 변수 (Lock Variable)
대기 큐 (Waiting Queue)
연산 (Operations)

Lock 요청: T1 스레드가 임계구역 진입을 위해 Lock 연산을 수행.
임계구역 실행: T1이 Lock을 획득하면 다른 스레드(T2, T3)는 큐에서 대기.
Unlock 수행: T1이 임계구역을 빠져나오며 Lock 해제 → 큐의 첫 번째 대기 스레드(T2)가 Lock을 획득.

T1 스레드의 Lock 연산
T2 스레드의 Lock 대기
T1의 Unlock 연산
대기 큐에서 T2 스레드 깨우기
락 변수: 임계구역 접근 상태를 관리.
대기 큐: Lock 해제 시 대기 스레드를 관리.
Lock/Unlock 연산: 스레드가 임계구역을 안전하게 사용하도록 제어.
효율적 임계구역 관리
뮤텍스 락 변수
pthread_mutex_t lock; 선언으로 뮤텍스 락 생성.뮤텍스 조작 함수
pthread_mutex_init() : 뮤텍스 초기화.pthread_mutex_lock() : 뮤텍스 잠금 (Entry 코드).pthread_mutex_unlock() : 뮤텍스 해제 (Exit 코드).pthread_mutex_destroy() : 뮤텍스 제거.대기 큐 관리
pthread_mutex_t lock; // 뮤텍스 락 생성
pthread_mutex_init(&lock, NULL); // 뮤텍스 초기화
...
pthread_mutex_lock(&lock); // 임계구역 코드 시작
// 임계구역 코드
pthread_mutex_unlock(&lock); // 임계구역 코드 종료
...
pthread_mutex_destroy(&lock); // 뮤텍스 제거
busy-waiting 방식
뮤텍스와의 차이점
효율적인 사용
락 변수
연산

락 획득 시도 (lock 연산)
임계구역 실행
락 해제 (unlock 연산)
락 획득 시도 (lock 연산)
임계구역 실행
busy-waiting:
락 상태 전환:
Non-blocking 모델:
효율성:
비효율성:
스핀락을 사용하는 함수 및 변수는 POSIX 표준에 정의되어 있음.
pthread_spinlock_t lock;pthread_spin_init() : 스핀락 초기화.
pthread_spin_lock() : 스핀락 획득.
pthread_spin_unlock() : 스핀락 해제.
pthread_spin_destroy() : 스핀락 자원 해제.
pthread_spinlock_t lock; // 스핀락 변수 생성
pthread_spin_init(&lock, NULL); // 스핀락 초기화
pthread_spin_lock(&lock); // 스핀락 획득
// 임계구역 코드
pthread_spin_unlock(&lock); // 스핀락 해제
락이 잡히는 시간이 긴 경우
뮤텍스(Mutex): 락을 오래 유지해야 할 경우 적합.
스핀락(Spinlock): 락 잡는 시간이 짧은 경우 효율적.
단일 CPU 시스템
멀티 코어 시스템
사용자 응용 프로그램 및 커널 코드
뮤텍스(Mutex): 사용자 응용 프로그램에서 일반적으로 사용.
스핀락(Spinlock): 커널 코드나 인터럽트 서비스 루틴처럼 빠른 처리 필요.
주의 사항
| 구분 | 뮤텍스(Mutex) | 스핀락(Spinlock) |
|---|---|---|
| 대기 큐 | 있음 | 없음 |
| 블록 가능 여부 | 락이 잡혀 있으면 블록됨 (blocking) | 락이 잡혀 있어도 블록되지 않고 계속 락 검사 (non-blocking) |
| lock/unlock 연산 비용 | 저비용 | CPU를 계속 사용하므로 고비용 |
| 하드웨어 관련 | 단일 CPU에서 적합 | 멀티코어 CPU에서 적합 |
| 주 사용처 | 사용자 응용 프로그램 | 커널 코드, 인터럽트 서비스 루틴 |
멀티스레드 간 공유 자원 관리 기법.
n개의 공유 자원을 다수의 스레드가 효율적으로 사용할 수 있도록 제어.
자원:
대기 큐:
카운터 변수:
n (자원의 총 개수).P/V 연산:

현실 시스템:
소프트웨어 구현:
현실 시스템:
소프트웨어 구현:
세마포어는 공유 자원의 사용 가능 상태를 관리하여 여러 프로세스가 자원을 효율적으로 사용할 수 있도록 함.
카운터 변수:
대기 큐는 자원이 부족할 경우 접근을 조정함.

여러 개의 멀티스레드가 동시에 n개의 자원 풀에 접근하려는 상황을 설명.
자원 관리가 이루어지지 않으면 충돌이나 비효율적인 자원 사용이 발생할 수 있음.
세마포(semaphore)를 사용하여 멀티스레드가 자원을 원활히 사용하도록 제어.
작동 방식:

자원의 인스턴스 수:
대기 중인 스레드:
카운터 변수:
counter = -2: 대기 중인 스레드가 2개(T5, T6).P 연산:
V 연산:
대기 큐:
T1~T4: 현재 자원을 사용 중.
T5, T6: 자원 반환(V 연산)이 발생하면 차례로 자원을 할당받아 실행.
자원의 효율적 사용: 여러 스레드가 자원을 동시에 활용 가능.
대기 관리: 자원이 부족할 경우 큐를 통해 질서 있게 관리.
확장성: 자원 수와 대기 큐 크기를 조정하여 다양한 상황에 대응.

Sleep-wait 세마포
자원을 요청했지만 할당받지 못한 스레드는 대기 큐에 들어가 잠자기 상태로 전환.
자원이 반환되면 대기 큐에서 스레드를 깨워 자원을 할당.
Busy-wait 세마포
자원을 기다리는 동안 무한 루프로 자원의 생성을 계속 확인.
자원이 생길 때까지 CPU를 사용하며 대기.
Sleep-wait 방식:
counter-- : 자원 요청 시 감소.if (counter < 0) : 자원이 없으면 대기 큐에 스레드를 삽입.while (counter <= 0) : 자원이 생길 때까지 무한 루프.counter-- : 자원을 획득 시 감소.Sleep-wait 방식:
counter++ : 자원 반환 시 증가.if (counter <= 0) : 대기 큐에서 한 스레드를 깨움.Busy-wait 방식:
counter++ : 자원 반환 시 증가.Sleep-wait 세마포:
Busy-wait 세마포:
sem_t s;세마포 구조체로, 내부적으로 counter 변수와 대기 큐를 포함하여 자원 관리.sem_init()
counter)로 초기화.sem_destroy()
sem_wait()
sem_trywait()
counter-- 후 0 반환.sem_post()
counter++.sem_getvalue()
counter 값을 반환.sem_t sem; // 세마포 구조체 생성
sem_wait(&sem); // P 연산. 자원 요청 (가용 자원이 없으면 대기).
// ... 자원 사용 ...
sem_post(&sem); // V 연산. 자원 반환.
sem_destroy(&sem); // 세마포 파괴.
여러 개의 자원을 관리.
Counter 변수로 관리하는 방식.예: 세마포의 counter가 n이라면 최대 n개의 스레드가 자원을 사용할 수 있음.
한 개의 자원만을 관리하는 세마포.
뮤텍스(Mutex)와 기능적으로 유사.
Counter 값이 0 또는 1만 가질 수 있음.
세마포 변수 S
0 또는 1 값을 가지며, 초기값은 1.대기 큐
스레드 스케줄링 알고리즘이 필요.
자원이 사용 중일 때 대기하는 스레드가 큐에 저장됨.
두 가지 원자 연산
wait 연산 (P 연산): 자원 요청.
signal 연산 (V 연산): 자원 반환.
하나의 자원만을 관리하므로 간단한 동기화에 적합.
뮤텍스와 달리 소유권 개념이 없음.(뮤텍스는 소유한 스레드만 잠금을 해제할 수 있음.)
단순 자원 관리와 접근 제어에 효율적.

3개의 스레드:
T1과 T3는 공유 변수를 사용하며, 이를 세마포로 동기화.
T2는 공유 변수에 접근하지 않음.
T1 시작:
T3 도착:
T2 실행:
T1 실행 재개 및 종료:
T3 실행:
T3와 같이 높은 우선순위의 스레드가 T1(낮은 우선순위) 때문에 블록되며, 그 사이에 T2(중간 우선순위)가 실행됨.
이로 인해 높은 우선순위의 작업이 불필요하게 지연됨.
정의: 공유 자원을 사용하는 스레드의 우선순위를 미리 정해진 높은 우선순위로 일시적으로 올려주는 방법이다.
특징:
정의: 공유 자원을 가진 스레드의 우선순위를 자원을 요청한 다른 스레드의 우선순위보다 높게 설정하여 빠르게 실행되도록 하는 방법이다.
특징:

구조 설명:
생산자 역할: 입력 스레드가 네트워크 또는 비디오 파일로부터 데이터를 읽어들인다.
버퍼: 생산자가 데이터를 버퍼에 저장하며, 이 버퍼는 소비자(재생 스레드)와 공유된다.
소비자 역할: 재생 스레드가 버퍼에서 데이터를 읽어 디스플레이로 출력한다.
문제:
생산자가 데이터를 버퍼에 추가하기 전에 소비자가 버퍼를 읽으려 하면 빈 버퍼 문제 발생.
반대로 소비자가 데이터를 처리하기 전에 생산자가 버퍼를 가득 채우면 버퍼 오버플로우 문제 발생.
구조 설명:
생산자 역할: 입력 스레드가 비디오 파일에서 데이터를 읽고 버퍼에 저장.
버퍼: 데이터를 여러 소비자 스레드(송신 스레드)로 전달.
소비자 역할: 각 송신 스레드가 네트워크를 통해 데이터를 클라이언트(미디어 플레이어)로 전송.
문제:
여러 소비자가 동시에 버퍼를 읽으려 할 때 경쟁 조건 발생 가능.
생산자와 여러 소비자 간 데이터의 정확한 전달을 보장해야 하며, 이를 위해 적절한 동기화가 필요.
구조: 생산자 스레드와 소비자 스레드가 공유 버퍼를 통해 데이터를 주고받는다.
역할:
문제 핵심:
문제 1 - 상호 배제 해결
공유 버퍼는 여러 스레드가 동시에 접근할 수 없다.
해결:
문제 2 - 비어 있는 공유 버퍼 문제
문제 3 - 꽉 찬 공유 버퍼 문제

공유 버퍼 구조:
P1, P2, P3: 생산자 스레드.
C1, C2, C3: 소비자 스레드.
공유 버퍼: 데이터를 저장하는 공간으로 생산자가 데이터를 추가하고, 소비자가 읽는다.
동기화 포인트: 생산자와 소비자가 버퍼에 동시에 접근하지 않도록 관리.
공유 버퍼가 비어 있을 때 소비자가 데이터를 읽으려고 하면 문제가 발생.
소비자는 읽을 데이터가 없기 때문에 대기 상태가 되어야 함.

세마포어 R = 0 (버퍼 비어 있음).
소비자는 P 연산을 수행:
생산자가 데이터를 추가하면, V 연산으로 세마포어 값을 증가시키며 대기 중인 소비자를 깨운다.
생산자는 버퍼에 데이터를 추가하고 V 연산 수행:
깨워진 소비자는 다시 P 연산을 수행하여 공유 버퍼에서 데이터를 읽는다.
소비자 동작:
P 연산 수행.
세마포어 값이 0이면 대기(wait).
생산자가 데이터를 추가하면 V 연산으로 대기 상태 종료.
공유 버퍼에서 데이터를 읽는다.
생산자 동작:
데이터를 버퍼에 추가.
V 연산으로 세마포어 값을 증가.
대기 중인 소비자를 깨운다.
세마포어 값 R은 읽기 가능한 데이터의 개수를 나타냄.
P/V 연산으로 소비자와 생산자의 작업 순서를 효율적으로 동기화.
결과: 소비자와 생산자는 버퍼가 비어 있거나 가득 찼을 때 적절히 대기하거나 실행할 수 있다.
공유 버퍼가 꽉 찼을 때 생산자가 데이터를 쓰려고 하면 문제가 발생.'
생산자는 더 이상 데이터를 쓸 공간이 없기 때문에 대기 상태가 되어야 함.

세마포어 W = 0 (버퍼가 꽉 참).
생산자는 P 연산을 수행:
소비자가 데이터를 읽으면, V 연산으로 세마포어 값을 증가시키며 대기 중인 생산자를 깨운다.
소비자가 데이터를 읽기 전, P 연산을 수행:
생산자는 깨워진 후 P 연산을 다시 수행하고 공유 버퍼에 데이터를 쓴다.
생산자 동작:
P 연산 수행.
세마포어 값이 0이면 대기(wait).
소비자가 데이터를 읽어 V 연산을 수행하면 대기 상태 종료.
공유 버퍼에 데이터를 쓴다.
소비자 동작:
데이터를 읽기 전, P 연산 수행.
세마포어 값을 감소.
데이터를 읽은 후, V 연산으로 세마포어 값을 증가.
대기 중인 생산자를 깨운다.
세마포어 값 W는 쓰기 가능한 버퍼 공간의 개수를 나타냄.
P/V 연산으로 생산자와 소비자의 작업을 효율적으로 동기화.
결과: 생산자와 소비자는 버퍼가 비거나 꽉 찼을 때 적절히 대기하거나 실행할 수 있다.
📌생산자와 소비자 알고리즘
R: 버퍼에서 읽기 가능한 버퍼 개수(비어 있으면 0).
W: 버퍼에 쓰기 가능한 버퍼 개수(꽉 차 있으면 0).
M: 뮤텍스(Mutex)로 생산자와 소비자 모두 사용.
while(true) {
P(R); // 세마포 R에 대해 P 연산 수행
// 읽기 가능한 버퍼가 없으면 대기
뮤텍스(M)를 잠금;
공유 버퍼에서 데이터를 읽는다. // 임계구역 코드
뮤텍스(M)를 연다;
V(W); // 세마포 W에 대해 V 연산 수행
// 대기 중인 생산자를 깨운다.
}
P(R):
뮤텍스 잠금:
뮤텍스 해제:
V(W):
while(true) {
P(W); // 세마포 W에 대해 P 연산 수행
// 쓰기 가능한 버퍼가 없으면 대기
뮤텍스(M)를 잠금;
공유 버퍼에 데이터를 저장한다. // 임계구역 코드
뮤텍스(M)를 연다;
V(R); // 세마포 R에 대해 V 연산 수행
// 대기 중인 소비자를 깨운다.
}
P(W):
뮤텍스 잠금:
뮤텍스 해제:
V(R):
세마포 R:
세마포 W:
뮤텍스 M: