프로세스를 동기화하지 않으면 코드가 예기치 않게 동작할 수 있다... 그래서 동기화를 위한 기법들이 있음.
상호 배제를 위한 동기화 도구(자물쇠 역할),
마치 자물쇠의 역할을 수행. == 뮤텍스락, 상호 배제를 위한 동기화 도구
단순한 형태 : 전역 변수 하나, 함수 두개
public class MutexExample {
private static Boolean lock = false; // 공유하는 락 객체
void acquire() {
synchronized (lock) { // synchronized 블록으로 임계 구역에 진입
// 임계 구역에 들어가기 전에 필요한 초기화나 확인 작업 수행
// 락이 해제될 때까지 기다림 (lock이 false일 때)
while (lock) {
}
}
lock = true; // 락을 획득
}
}
void release() {
synchronized (lock) {
// 임계 구역을 빠져나간 후에 필요한 정리 작업 수행
lock = false; // 락을 해제
}
}
}
acquire(); // 잠겨있는지 확인, 잠겨있지 않다면 잠그고 들어가기
// 임계구역 진입 ( 이전 장의 '총합' 변수 접근)
release(); // 자물쇠 반환!
syncronized라는 키워드가 붙었습니다. syncronized로 선언된 메서드를 호출하기 위해서는 메서드를 실행하기 위한 락을 획득해야 합니다.
만일 락을 획득할 수 없다면 (다른 스레드가 syncronized 메서드를 실행 중에 있다면) 락을 획득하지 못한 스레드는 그대로 대기 상태가 됩니다.
syncronized 메서드를 실행하는 스레드가 메서드 실행을 종료하면 락이 해제되고, 대기 상태에 있던 스레드가 깨어나 실행을 재개할 수 있게 됩니다.
// 락이 해제될 때까지 기다림 (lock이 false일 때)
while (lock) {
try {
lock.wait(); // 락을 해제하고 다른 스레드로부터의 알림을 기다림
} catch (InterruptedException e) {
e.printStackTrace();
}
뮤텍스락과 비슷하지만, 조금 더 일반화된 방식의 동기화 도구이다.
뮤텍스락은 공유자원에 대해 하나인 경우, 세마포는 공유자원이 여러개인 경우이다.
세마포 종류에도 이진 세마포(binary semaphore)와 카운팅 세마포가 있다. 이진 세마포는 뮤텍스 락과 비슷한 개념이다.
세마포는 철도 신호기에 유래한 단어이다. 마치 프로세스가 임계구역에 진입할때 신호를 보고 진입여부를 판단한다.
세마포의 단순한 형태 : 전역 변수 하나, 함수 두개
wait()
// 임계구역
signal()
wait(){ // 만일 임계 구역에 진입할 수 있는 프로세스 갯수가 0 이하라면
while ( S < = 0 ){
} // 지속 확인
S--; // 임계 구역에 진입할 수 있는 프로세스 갯수가 하나 이상이면 S를 1 감소 시키고 임계 구역 진입!!
}
signal(){
S++ // 임계구역 작업을 끝낸후 S 를 1 증가
}
만약 세개의 프로세스 P1,P2,P3가 두개의 공유자원(S == 2) 에 접근한다고 가정..
1. P1은 wait호출, S==2 이므로 S-- 하고 임계진입
2. P2은 wait호출, S==1 이므로 S-- 하고 임계진입
3. P3은 wait호출, S==0 무한 반복하며 S 확인
4. P1 임계구역 종료, signal()호출, S 1 증가
5. P3가 S가 1이 됨을 확인, S--하고 임계진입
분명히 문제가 있다. 공유자원이 없는 경우 무한히 반복하여 확인하는것 바쁜대기..(Busy waiting)..
그래소 실제로는 S<=0일 경우, 즉 사용할 수 있는 자원이 없는 경우 대기상태로 만든다.(해당 프로세스의 PCB를 대기 큐에 삽입 시킴)
이후, 자원이 생겼을 때 대기 큐의 프로세슬르 준비상태로 만듦.
(해당 프로세스 PCB를 대기큐에서 꺼내 준비 큐에 삽입)
void wait(Process p) {
S--;
if (S < 0 ) {
addToQueue(p); // add this process to Queue, 해당 프로세스를 대기큐에 삽입
sleep(); // 대기상태로 만든다.
}
}
void signal(Process p) {
S++;
if (S < = 0 ) {
removeQueue(); // 대기큐에서 제거.
wakeup(p); // 프로세스를 준비큐로 옮긴다.
}
}
바뀐 로직으로 다시 실행을 수행하면
1. P1은 wait호출 S--, S는 1 이므로 임계진입
2. P2은 wait호출, S--, S는 0 이므로 하고 임계진입
3. P3은 wait호출, S--, S는 -1 이므로 PCB를 대기큐에 넣고 대기전환
4. P1 임계구역 종료, signal()호출, S 1 증가 , S는 0인 상태, P3를 준비 큐로 이동.
5. 깨어난 P3가 임계진입
6. P2 임계구역 종료, signal()호출, S 1 증가 , S는 1인 상태
7. P3 임계구역 종료, signal()호출, S 1 증가 , S는 2인 상태
세마포도 뮤텍스 락과 마찬가지로 많은 프로그래밍언어에서 사용
세마포 처럼 매번 임계구역 앞뒤로 wait(), signal()을 호출해야하나??
그래서 나온것이 모니터
자바에서 활용, 사용자(개발자)가 다루기에 편한 동기화 도구이다.
모니터는 공유자원과 공유자원에 접근하기 위한 인터페이스(통로)를 묶어서 관리한다. 그리고 프로세슨느 반드시 인터페이스를 통해서만 자원에 접근할 수 있다.
상호 배제를 위한 큐 와 조건 변수에 대한 큐는 다르다
전자는 모니터에 하나만의 프로세스만 진입하도록 만들어진 큐
후자는 모니터에 진입한 프로세스의 실행조건이 만족될때까지 잠시 실행이 중단되어 기다리기 위해 만들어진 큐
조건 변수 이용 (condition variable)
프로세스나 스레드의 실행 순서를 제어하기 위해 사용하는 특별한 변수
조건변수.wait() : 대기상태로 변경, 조건변수에 대한 큐에 삽입
조건변수 signal() : wait()로 대기상태로 접어든 조건변수를 실행상태로 변경 :
x.wait()가 호출이되면 조건 변수 x에 대한 큐에 삽입되고 모니터는 비게된다. 이후 다른 프로세스에서 x.signal()을 호출시 x 프로세스는 대기상테에서 준비상태로 변경후 모니터 안으로 들어오게 된다.
이외에도 어떤프로세스가 잠깐 중단후 signal을 불러와 x 프로세스가 끝난후에 signal을 호출 할 수 도 있따.
정리하면, 모니터 안에는 하나의 프로세스만 존재 할 수 있따.
- wait()를 호출 했던 프로세스는 signal()을 호출한 프로세스가 모니터를 떠난 뒤에 수행을 재개
- siganl()을 호출한 프로세스의 실행을 일시 중단하고 자신이 실행된 뒤 다시 signal()을 호출한 프로세스의 수행을 재개
뛰어난 글이네요, 감사합니다.