4. 병행 프로그램의 올바른 동작
• 변수 하나에 여러 프로세스가 값을 덮어쓰
려고 하는게 문제를 복잡하게 하고 있다.
• 상태 변화가 병행으로 일어날 때 예측 가
능하게 동작하도록 병행 처리에 제약을 준
다.
5. 병행 처리에 제약을 주는 방법
• 상태 변수 하나를 같이 사용
– 값을 바꿀 수 있는 연산 두 개가 동시에 돌아가지
못하게 제약
• 차례대로 돌아가게 만들어 병행 프로그램과
같은 결과를 만들어 낸다.
– 어떤 차례인지 결정하지 않으면 올바른 결과가 여
러 개다.
• 열의 퍼짐 시뮬레이션(flow of heat in an object)
– 여러 개의 프로세스
– 자기 값과 이웃 값의 평균으로 자기 값 수정
– 되풀이 하다보면 올바른 값으로 수렴
6. 병행 프로세스를 다루기 어려운 까닭
• 여러 프로세스에서 일어나는 사건의 차례
가 서로 뒤엉킬 수 있기 때문
• 모든 경우를 하나하나 따져봐야 한다.
• 사건이 많아질수록 경우의 수가 폭발적으
로 증가한다.
– (a,b,c,d,e,f) 사건이 차례대로 일어나는 A 프로
세스와 (x,y,z,w) 사건이 차례대로 일어나는 B
프로세스가 있다면???
7. parallel-execute
(define x 10)
(parallel-execute (lambda () (set! x (* x x))) ; P1
(lambda () (set! x (+ x 1)))) ; P2
P1 : a (첫 번째 x 읽기), b (두 번째 x 읽기), c (x를 덮어쓰기)
P2 : ㄱ (x 읽기), ㄴ (x를 덮어쓰기)
두 프로세스가 끝난 다음 나올 수 있는 x의 값
101 : a b c ㄱ ㄴ
121 : ㄱ ㄴ a b c
110 : a ㄱ ㄴ b c
11 : ㄱ a b c ㄴ
100 : a b ㄱ ㄴ c
8. 병행성을 다스리는 방법
• 서로 뒤엉켜 돌아가는 여러 프로세스에 어
떤 제약을 걸어서, 프로그램이 제대로 돌
아갂다고 믿을 수 있도록 병행 시스템을
설계하는 방법이 필요하다.
• 줄 세우개(serializer)가 한가지 방법.
10. make-serializer
(define x 10)
(define s (make-serializer))
(parallel-execute (s (lambda () (set! x (* x x)))) ; P1
(s (lambda () (set! x (+ x 1))))) ; P2
P1 : a (첫 번째 x 읽기), b (두 번째 x 읽기), c (x를 덮어쓰기)
P2 : ㄱ (x 읽기), ㄴ (x를 덮어쓰기)
두 프로세스가 끝난 다음 나올 수 있는 x의 값
101 : a b c ㄱ ㄴ
121 : ㄱ ㄴ a b c
110 : a ㄱ ㄴ b c
11 : ㄱ a b c ㄴ
100 : a b ㄱ ㄴ c
11. 줄 세우개를 사용한 은행 계정
• 잒액을 변경하는 withdraw, deposit 프로
시저를 같은 줄 세우개를 사용해 동시에
실행할 수 없게 제한했다.
• 줄 세우개는 계정마다 하나씩 가진다.
– 서로 다른 계정의 작업은 병행으로 이루어진
다.
13. 연습 3.41
balance에 손을 댈 때 줄 세우기를 하지 않으면 제대로 돌아가지 않을
수도 있다는데 동의?
(define (make-account balance)
(define (withdraw amount)
(if (>= balance amount)
(begin (set! balance (- balance amount))
balance)
"Insufficient funds"))
(define (deposit amount)
(set! balance (+ balance amount))
balance)
(let ((protected (make-serializer)))
(define (dispatch m)
(cond ((eq? m 'withdraw) (protected withdraw))
((eq? m 'deposit) (protected deposit))
((eq? m 'balance)
((protected (lambda () balance)))) ; serialized
(else (error "Unknown request -- MAKE-ACCOUNT" m))))
dispatch))
14. 연습 3.41 풀이
• 줄 세우기를 한 결과 withdraw, deposit과
balance는 동시에 실행되지 않는다.
• balance는 값을 변경하는 동작이 아니라
값을 읽는 동작을 하는 프로시저.
• Ben은 balance를 거래 후 잒액으로 생각.
• 동의하지 않음.
• balance 프로시저를 실행하는 그 시갂에
잒액을 보여줘야 한다고 생각한다.
15. 연습 3.42
이렇게 만들어도 괜찮은가? 변경 젂 make-account와 병행성에 어떤
차이가 나는가?
(define (make-account balance)
(define (withdraw amount)
(if (>= balance amount)
(begin (set! balance (- balance amount))
balance)
"Insufficient funds"))
(define (deposit amount)
(set! balance (+ balance amount))
balance)
(let ((protected (make-serializer)))
(let ((protected-withdraw (protected withdraw))
(protected-deposit (protected deposit)))
(define (dispatch m)
(cond ((eq? m 'withdraw) protected-withdraw)
((eq? m 'deposit) protected-deposit)
((eq? m 'balance) balance)
(else (error "Unknown request -- MAKE-ACCOUNT" m))))
dispatch)))
16. 연습 3.42 풀이
• 상관 없다.
• 이젂과 상관없이 make-serializer는 은행 계정
하나당 하나가 존재한다.
• make-serializer 프로시저를 실행하는 프로시
저를 미리 만들어서 같은 프로시저를 내놓는
다고 하더라도 줄 세운 프로시저가 같이 쓰는
뮤텍스와 같은 동기화 수단에 의존하기 때문
• 그렇기 때문에 병행성 또한 차이가 없다.
• 뒤에 make-serializer 구현 참조.
17. 여러 자원을 함께 쓰는 문제
• 이제 까지 자원 하나를 여럾이 쓰는 예제.
– 폴과 피터가 같이 쓰는 생활비 은행 계좌
• 여러 자원을 함께 쓰는 경우를 살펴보자.
– 두 은행 계정의 남은 돈을 맞바꾸는 일.
18. exchange
(define (exchange account1 account2)
(let ((difference (- (account1 'balance)
(account2 'balance))))
((account1 'withdraw) difference)
((account2 'deposit) difference)))
• difference 변수에 값을 할당 후 withdraw,
deposit 프로시저 호출 젂에 account1,
account2의 balance가 변경될 수 있다.
19. 해결 방법
• 두 계정의 줄 세우개를 사용해 exchange
프로시저 젂체를 줄 세운 프로시저로 만든
다.
• 계정 속의 줄 세우개를 밖으로 노출 시켜
야 한다.
– 모듈 방식이 깨짐
– 현재로선 다른 방법이 없다.
21. serialized-exchange
(define (serialized-exchange account1 account2)
(let ((serializer1 (account1 'serializer))
(serializer2 (account2 'serializer)))
((serializer1 (serializer2 exchange))
account1
account2)))
• 두 계정의 줄 세우개 serializer1, serializer2
를 사용해 exchange 프로시저 젂체를 줄
세운 프로시저로 만들었다.
22. 연습문제 3.44
돈을 맞바꾸는 문제처럼 transfer 프로시저 자체를 줄 세운 프로시저
로 만들어야 하는가?
(define (transfer from-account to-account amount)
((from-account 'withdraw) amount)
((to-account 'deposit) amount))
23. 연습문제 3.44 풀이
• 필요 없다.
• 돈을 맞바꾸는 경우에는 두 은행 계정의
남은 돈의 차이를 계산한 후에 인터럽트가
걸린 경우 두 은행 계정의 남은 돈 차이가
변할 수 있기 때문에 돈을 맞바꾸는 프로
시저 젂체를 줄 세운 프로시저로 만들었다.
• 돈을 옮기는 프로시저의 경우 인터럽트가
걸려도 프로시저 동작에 이상 없다.
24. 연습문제 3.45
serialized-exchange를 불러 쓸 때 어떤 일이 일어나는지 따져라
(define (make-account-and-serializer balance)
(define (withdraw amount)
(if (>= balance amount)
(begin (set! balance (- balance amount))
balance)
"Insufficient funds"))
(define (deposit amount)
(set! balance (+ balance amount))
balance)
(let ((balance-serializer (make-serializer)))
(define (dispatch m)
(cond ((eq? m 'withdraw) (balance-serializer withdraw))
((eq? m 'deposit) (balance-serializer deposit))
((eq? m 'balance) balance)
((eq? m 'serializer) balance-serializer)
(else (error "Unknown request -- MAKE-ACCOUNT" m))))
dispatch))
25. 연습문제 3.45 풀이
• serialized-exchange 프로시저는 두 계정의
줄 세우개로 프로시저 젂체를 줄 세운 프
로시저로 만든다.
• 안에서 exchange 프로시저를 실행하는데,
withdraw, deposit 프로시저가 실행되지
않고 하염없이 기다린다.
• 계정의 줄 세우개로 프로시저를 실행했기
때문에.
26. make-serializer 구현
• 뮤텍스 (mutex)
– mutual exclusion : 상호 배제
– 뮤텍스 물체를 쥐거나(acquired) 풀어 주는
(release) 연산
– 한 프로세스가 뮤텍스를 잡으면 풀어 놓을 때
까지 다른 프로세스가 뮤텍스를 잡지 못한다.
• 줄 세우개 하나에 뮤텍스 하나.
28. test-and-set! 구현
(define (test-and-set! cell)
(if (car cell)
true
(begin (set-car! cell true)
false)))
• 이렇게 구현하면 제대로 동작 안 한다.
• 병행성 제어를 집어넣어야 할 곳
• 한 알갱이로(atomically) 처리되어야 한다.
– 하드웨어 수준에서 지원하는 프로시저를 사용.
– MIT Scheme : without-interrupts
29. 연습문제 3.47
세마포어(semaphore)를 뮤텍스와 test-and-set!을 써서 만
들어라.
- 잡았다 놓았다 하는 연산은 뮤텍스와 같지만 n개 프로세
스가 병행으로 세마포어를 쥘 수 있다.
- 뮤텍스를 사용하는 것만 풀이
31. 데드락 (deadlock)
• 여러 프로세스가 다른 프로세스를 지켜보
면서 아무것도 하지 못하고 끝없이 기다려
야 하는 상태
• 돈 맞바꾸는 문제의 경우에는 계정마다 번
호를 매겨 놓고 모든 프로세스가 언제나
번호가 낮은 계정을 움켜쥐고 일을 시작하
도록 하면 해결 가능.
32. 연습문제 3.48
계정에 번호를 매겨, 각 프로세스가 처음에 더 작은 번호
를 매긴 계정을 움켜쥐도록 만들어서 데드락을 피하는 기
법을 사용.
왜 돈 맞바꾸는 문제에서 데드락에 빠지지 않는가?
그 방법대로 serialized-exchange 프로시저를 작성.
33. 연습문제 3.48 풀이
(define (make-account-and-serializer id balance)
(define (withdraw amount)
(if (>= balance amount)
(begin (set! balance (- balance amount))
balance)
"Insufficient funds"))
(define (deposit amount)
(set! balance (+ balance amount))
balance)
(let ((balance-serializer (make-serializer)))
(define (dispatch m)
(cond ((eq? m 'withdraw) withdraw)
((eq? m 'deposit) deposit)
((eq? m 'balance) balance)
((eq? m 'id) id)
((eq? m 'serializer) balance-serializer)
(else (error "Unknown request -- MAKE-ACCOUNT" m))))
dispatch))
34. 연습문제 3.48 풀이(계속)
(define (serialized-exchange account1 account2)
(let ((serializer1 (account1 'serializer))
(serializer2 (account2 'serializer)))
(if (< (account1 'id) (account2 'id))
((serializer2 (serializer1 exchange)) account1 account2)
((serializer1 (serializer2 exchange)) account1 account2))))
• 비교 대상이 되는 id가 있어서 어떠한 계
정 두개를 가져다 놓더라도 줄 세우는 방
법은 한가지 밖에 없다. 그래서 데드락에
빠지지 않는다.