2023. 3. 20. 16:32ㆍ운영체제
1. 세마포 : 프로세스 동기화 문제 해결을 위한 소프트웨어 도구
① P(=acquire) : 세마포 내부의 정수가 1 감소. 값이 음수이면, block상태(메소드 실행 도중에 콘텍스트 스위칭이 일어나서 다른 메소드가 실행되는 일은 없다)
② V(=release) : 세마포 내부의 정수가 1 증가. 값이 음수/0이면, ready queue로 보내어 wake up
☞세마포 내부 정수 값이 음수일 때, 그 절대값은 현재 세마포 내부큐에서 블록 되어있는 프로세스의 개수
☞세마포의 내부 정수 값(=number of permit)은 P 동작을 호출했는데도 블록되지 않고 계속 진행할 수 있는 프로세스 개수
2. 세마포를 사용한 동기화
1) 상호배타 : 임계구역에 최대 하나의 프로세스만. 즉, number of permit=1
Semaphore sem=new Semaphore(1); //number of permit=1
sem.acquire();
//////////////
Critical-Section
//////////////
sem.release();
ex) 세마포를 사용한 상호배타 조건의 만족 (순서)
2) 실행 순서 제어
ex) P1: S1(statement);
P2: S2;
▶ 둘 중 하나가 선택되어 프로세스가 실행될 것인데, P1이 먼저 선택되면 S1코드 먼저, P2가 먼저 선택되면 S2코드 먼저 실행된다.
ex) P1: S1; sem.release();
P2: sem.acquire(); S2;
▶ number of permit=0;으로 초기값을 두고, 둘 중 어떤 것이 선택되어도 S1이 먼저 실행된다.
import java.util.concurrent.Semaphore;
class BankAccount{ //은행계좌 클래스
int balance; //현재 잔액
Semaphore sem=new Semaphore(1); //for 상호배타
Semaphore sem2=new Semaphore(0); //for 진행. parent 항상 먼저
void deposit(int amount){ //입금
try{
sem.acquire();
} catch(InterruptedException e) {}
//////////////////////////////////
balance=balance+amount; //parent의 임계구역
System.out.print("+");
//////////////////////////////////
sem.release();
sem2.release();
}
void withdraw(int amount){ //출금
try{
sem2.acquire();
sem.acquire();
} catch(InterruptedException e) {}
//////////////////////////////////
balance=balance-amount; //child의 임계구역
System.out.print("-");
//////////////////////////////////
sem.release();
}
int getBalance(){ //잔액조회
return balance;
}
}
class Parent extends Thread{ //부모 쓰레드
BankAccount b;
int count;
Parent(BankAccount b, int count){
this.b=b;
this.count=count;
}
@Override
public void run(){ //1원씩 count번 입금
for(int i=0;i<count;i++)
b.deposit(1);
}
}
class Child extends Thread{
BankAccount b;
int count;
Child(BankAccount b, int count){
this.b=b;
this.count=count;
}
@Override
public void run(){ //1원씩 count번 출금
for(int i=0;i<count;i++)
b.withdraw(1);
}
}
class Test{ //메인 프로그램
static final int MAX=5000; //입출금 횟수
public static void main(String[] args) throws InterruptedException{
BankAccount b=new BankAccount();
Parent p=new Parent(b,MAX);
Child c=new Child(b,MAX);
p.start();
c.start();
p.join();
c.join();
System.out.println(b.getBalance());
}
}
위의 코드를 실행하면 CPU의 프로세스 선택 순서에 관계없이 항상 parent thread가 먼저 돌것이다.(=입금이 먼저)
ex) 부모: deposit(); sem2.release(); sem3.acquire();
자식: sem2.acquire(); withdraw(); sem3.release();
▶ 위는 입출금이 교대로 반복된다.
import java.util.concurrent.Semaphore;
class BankAccount{ //은행계좌 클래스
int balance; //현재 잔액
Semaphore sem=new Semaphore(1);
Semaphore sem2=new Semaphore(0);
Semaphore sem3=new Semaphore(0);
void deposit(int amount){ //입금
try{
sem.acquire();
//////////////////////////////////
balance=balance+amount; //parent의 임계구역
System.out.print("+");
//////////////////////////////////
sem.release();
sem2.release();
sem3.acquire();
} catch(InterruptedException e) {}
}
void withdraw(int amount){ //출금
try{
sem2.acquire();
sem.acquire();
} catch(InterruptedException e) {}
//////////////////////////////////
balance=balance-amount; //child의 임계구역
System.out.print("-");
//////////////////////////////////
sem.release();
sem3.release();
}
int getBalance(){ //잔액조회
return balance;
}
}
class Parent extends Thread{ //부모 쓰레드
BankAccount b;
int count;
Parent(BankAccount b, int count){
this.b=b;
this.count=count;
}
@Override
public void run(){ //1원씩 count번 입금
for(int i=0;i<count;i++)
b.deposit(1);
}
}
class Child extends Thread{
BankAccount b;
int count;
Child(BankAccount b, int count){
this.b=b;
this.count=count;
}
@Override
public void run(){ //1원씩 count번 출금
for(int i=0;i<count;i++)
b.withdraw(1);
}
}
class Test{ //메인 프로그램
static final int MAX=5000; //입출금 횟수
public static void main(String[] args) throws InterruptedException{
BankAccount b=new BankAccount();
Parent p=new Parent(b,MAX);
Child c=new Child(b,MAX);
p.start();
c.start();
p.join();
c.join();
System.out.println(b.getBalance());
}
}