2021. 6. 13. 20:36ㆍIT
0. Semaphore
- 여러 개의 프로세스가 공유 자원에 동시 접근할 때 문제 발생
- 프로세스 A, 프로세스 B는 int a = 100 이라는 자원을 공유하여, 각각 a를 1씩 증가시키는 작업을 시킴
- 이때, 한번에 둘 이상의 프로세스가 접근하여 데이터를 동시에 변경하며 결과값에 영향을 줄 수 있음
- 경쟁 상태= Race condition
- 공유된 자원 속 하나의 데이터는 한번에 프로세스만 접근할 수 있도록 제한을 두어야 할 때 고안된 것이 Semaphore (쎄마포어)
- Semaphore는 P연산과 V연산으로 이루어져 있음
- P(S) : S를 1 감소 (S--)
- V(S) : S를 1 증가 (S++)
- 프로세스는 S가 1 이상일 때만 임계영역으로 진입할 수 있다고 가정 / S가 0이면 진입하지 못함
* Semaphore in JAVA
- acquire 메서드
- lock 확보
- 세마포어는 S를 검사
- 만약 S가 1 이상이면 스레드 임계구역 진입 허가
- 만약 S가 1보다 작으면 리스트에 저장 후 일시 정지
- release 메서드
- 공유자원을 점유한 스레드가 임계구역에서 나올 경우
- S++
- 대기 중인 스레드 중에서 하나를 깨우고, 깨어난 스레드는 임계구역에 들어감
- 공유자원을 점유한 스레드가 임계구역에서 나올 경우
은행 계좌 문제 (자바 코드)
Deposit.java (예금)
public class Deposit implements Runnable {
Account account; // 공유 객체
public Deposit(Account account) {
this.account = account;
}
@Override
public void run() {
for(int i=0; i<Bank.LOOP; i++) {
account.deposit(10);
}
}
}
WithDraw.java (인출)
public class WithDraw implements Runnable {
Account account;
public WithDraw(Account account) {
this.account = account;
}
@Override
public void run() {
for(int i=0; i<Bank.LOOP; i++) {
account.withDraw(10);
}
}
}
Account.java (계좌)
import java.util.concurrent.Semaphore;
public class Account {
private int balance = 0; // 잔액
Semaphore s, with, depo; // 세마포어 객체 참조변수
public Account(Semaphore s, Semaphore with, Semaphore depo) {
this.s = s;
this.with = with;
this.depo = depo;
}
// deposit- 예금
public void deposit(int money) {
try {
s.acquire();// 세마포어 객체를 통한 동기화 검사
// S를 검사함. S가 1이면 스레드가 CS에 들어가는 것을 허용
// 임계 영역(critical section)에 들어가서
System.out.println(Thread.currentThread().getName()+" : " + money+"원");
balance += money;
System.out.println("현재 잔액 : " + balance + "원");
System.out.println();
s.release(); // Lock 해제
with.release();
depo.acquire(); // 입금후에는 반드시 출금을 해야 하므로 자신을 블록함
} catch (InterruptedException e) {}
}
// withDraw - 인출
public void withDraw(int money) {
try {
with.acquire(); // 세마포어 변수 0이므로 대기. 입금보다 먼저 수행하는 것을 막음
s.acquire(); // 세마포어 객체를 통한 동기화 검사
// 임계영역
System.out.println(Thread.currentThread().getName() + " : " + money + "원");
balance -= money;
System.out.println("현재 잔액 : " + balance + "원");
System.out.println();
s.release(); // Lock 해제
depo.release();
// 출금 수행이 완료되면 block되었던 입금 프로세스를 깨워줌
}
catch(InterruptedException e) {} }
public void printBalance() {
// 잔액 출력
System.out.println("현재 잔액 : " + balance);
}
}
Bank.java (은행)
import java.util.concurrent.Semaphore;
public class Bank {
static final int LOOP = 2;
public static void main(String[] args) {
// 세마포어 객체 생성 (permit = 1: 공유자원 1개, fair = true : FIFO)
Semaphore s = new Semaphore(1, true);
Semaphore s_1 = new Semaphore(0, true);
Semaphore s_2 = new Semaphore(0, true);
Account account = new Account(s, s_1, s_2); // 공유 객체 생성
// 스레드 생성
Thread depositThread1 = new Thread(new Deposit(account));
Thread withDrawThread1 = new Thread(new WithDraw(account));
Thread depositThread2 = new Thread(new Deposit(account));
Thread withDrawThread2 = new Thread(new WithDraw(account));
// 스레드 이름 설정
depositThread1.setName("user1 입금");
withDrawThread1.setName("user1 출금");
depositThread2.setName("user2 입금");
withDrawThread2.setName("user2 출금");
// 스레드 실행
depositThread1.start();
withDrawThread1.start();
depositThread2.start();
withDrawThread2.start();
// 스레드 정지
try {
// join을 사용하지 않는다면
// 메인시작 ~ 메인 끝나도 스레드 백그라운드에서 돌아감. 메인은 상관없이 종료
// join을 사용한다면
// 스레드 끝날떄까지 기다렸다가 메인이 끝남
depositThread1.join();
withDrawThread1.join();
depositThread2.join();
withDrawThread2.join();
}
catch(InterruptedException e) {}
// 잔액 출력
account.printBalance();
}
잠깐! Ordering이란 ❔❓
- 입금 출금 순서가 안 지켜졌을 경우
- 혹시 아래 Account.java 코드에서
deposit 메소드의 with.release(), depo.acquire(); 와
withDraw 메소드의 with.acquire(); deop.release(); 코드 보이시나요? (가독성 죄송합니다..;0;)
이 네개를 일단 처음에 쓰지 않고 Bank.java에서 print를 실행하면
총 잔액이 0원이 되긴 하지만 그 과정에서 user1, user2 뿐만 아니라 입금 출금의 순서가 뒤죽박죽인 걸 볼 수 있습니다!
우리의 통장이 마이너스 통장이 되는 걸 그냥 보고만 있을 수 없죠!!
이때, Ordering Semaphore가 필요합니다.
다시 위의 deposit 메소드의 with.release(), depo.acquire(); 와
withDraw 메소드의 with.acquire(); deop.release(); 코드 를 그대로 적용해 보시면
user1 입금 user2 입금 user1 출금 user2 출금 순으로 잘 작동되는 걸 보실 수 있습니다.
deposit 메소드에선
Semaphore 변수(S)가 0인 with.release()를 해주어 출금하라고 출금 메소드를 깨워주고(s++)
depo.acquire()는 변수가 0이므로 출금을 하려고 하면 일시정지를 하게 하는 거죠!!
마찬가지로 withdraw(인출) 메소드에선
Semaphore 변수(S)가 0인 with.acquire()를 통해 출금을 먼저 하려고 하면 일시 정지하게 하고
depo.release()를 해 주어 출금 수행이 완료되면 잠자고 있던 입금 프로세스를 깨워줍니다
1. Mutex vs Semaphore
Mutex
- lock, unlock의 상태만 존재하는 일종의 key를 기반으로 하는 이진 세마포어
Semaphore
- 여러개의 프로세스가 동시에 공유자원에 접근할 수 있음
- S =2 , 임계영역에 들어갈 수 있는 프로세스는 2개가 됨
'IT' 카테고리의 다른 글
[OS] CPU 스케줄링 (0) | 2021.06.14 |
---|---|
[OS] Monitor (모니터) / synchronized 이용 은행 계좌 문제 (0) | 2021.06.13 |
[OS] 프로세스 동기화 (Process Synchronization)) (0) | 2021.06.13 |
[OS] 메모리 관리 요약 (0) | 2021.06.13 |
[OS] 자식 프로세스 2개 생성 , kill 명령어 실행하기 (0) | 2021.05.17 |