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