[#10] 프로세스 동기화-3

Posted by Neph's Blog on April 25, 2021

Bounded - Buffer Problem(Producer - Consumer Problem)

image-20210425021405828

생산자가 데이터를 공유 버퍼에 넣을때, 소비자가 공유버퍼에서 데이터를 꺼내갈 때 버퍼가 비어있거나 꽉 찬 경우에 문제가 발생할 수 있다. 이 문제는 공유데이터에 Lock을 걸었다가 풀었다가 하는 방식으로 해결할 수 있다.

이 방식을 이용하기 위해 mutex와 resource count가 사용되는데 mutex는 공유 버퍼 자체에 Lock을 걸었다 풀었다 하는 방식을 사용하기 위해 필요하며 resource count는 버퍼의 크기를 관리하기 위해 사용된다. Producer와 Consumer는 포인터 형식으로 관리되며 ppt의 그림을 통해 원형 큐의 방식으로 관리될 것임을 유추할 수 있다.

image-20210425022704389

위 문제를 수도코드로 표현한 것이다.

full이라는 변수는 내용이 들어있는 버퍼의 개수를 세기 위한 변수, empty는 비어있는 버퍼의 개수를 세기 위한 변수이다.

버퍼에 내용을 집어넣으려는 시도를 하는 Producer는 빈 버퍼가 있는지를 먼저 확인하는 작업을 거치며

V(full)을 통해 내용이 들어있는 버퍼의 개수를 증가시키는 일을 한다.

Consumer는 내용이 들어있는 버퍼가 있는지를 먼저 확인한 뒤 V(empty)를 통해 비어있는 버퍼의 개수를 증가시킨다.

Readers and Writers Problem

image-20210425112421207

쓰기 작업은 다른 process와 동시에 하면 안되지만 읽기 작업은 많은 프로세스가 동시에 시도해도 무관하다.

하지만 쓰기 작업을 하면서 Lock을 거는 방식을 사용하면 단지 읽기만 하려했던 프로세스들이 공유 데이터(DB)에 접근할 수 없게되는 문제가 발생한다.

image-20210425113106309

위의 코드에서 DB는 공유 데이터를 나타내며 db는 DB에 Lock을 걸기위한 semaphore 변수이다.

Reader의 경우에 DB에 Writer와 같은 방식으로 Lock을 걸었다 풀었다 하는 작업을 하는 대신 (비효율적이므로)

readcount라는 변수를 두어서 현재 reader의 수를 관리한다. (readcount라는 변수도 공유변수이기 때문에 mutex를 이용하여 관리한다.)

만약 자신이 마지막 reader였다면 DB의 Lock을 그제서야 풀어주는 방식을 사용하는데 이러한 방식은 Writer의 Starvation 문제를 불러올 수 있다. 이러한 문제는 Timer 방식과 유사하게 해결할 수 있다.

Dining - Philosophers Problem

image-20210425113838761

철학자가 하는 일은 생각하기, 밥먹기

왼쪽과 오른쪽의 젓가락을 모두 잡아야 밥을 먹을 수 있다고 가정

젓가락을 잡는 연산은 P(chopstick[i]) (왼쪽 젓가락을 잡는 작업)과 P(chopstick[i+1]%5) (오른쪽 젓가락을 잡는 작업) 으로 진행된다.

위 방식의 문제점은 deadlock 현상이 발생할 수 있다는 것인데 모두가 동시에 왼쪽 젓가락을 드는 순간 그 누구도 밥을 먹을 수 없는 상황이 발생한다.

image-20210425114125945

deadlock 현상을 해결하기 위한 방안들

4명만이 동시에 식탁에 앉도록 하여 해결

모든 젓가락을 잡을 수 있음이 확실시 된 상황에서만 젓가락을 집을 수 있도록 하여 해결

짝수, 홀수 번호를 구분하여 해결

image-20210425114544075

철학자 i가 실행하는 함수는 pickup, eat, putdown, think

self 배열은 양쪽 젓가락을 모두 잡을 수 있는지를 판단하는 변수이다. 0이면 불가 1이면 가능 등..

state를 나타내는 enum 변수는 코딩을 돕기 위해 철학자의 현재 상태를 나타낸다.

state도 공유변수로 사용되므로 Lock을 걸어주는 작업이 필요하다

test 함수는 젓가락을 내려놓을 때 인접한 위치의 철학자들이 젓가락 pickup을 위해 기다리고 있던 상황이라면 그 철학자가 젓가락을 얻을 수 있도록 해준다. (semaphore에 약간 어긋나는 부분이 있다.)

Monitor

image-20210425114332082

image-20210425115642342

Semaphore는 여러 불편한 점들이 있는데

프로그래머가 실수를 하면 안된다는 치명적인 문제를 가지고 있다

한번의 실수가 시스템에 치명적이며 이러한 버그가 발생했을 때 고치기가 쉽지 않다

이를 해결하기 위해 Monitor 방식을 사용하면 이러한 문제들을 어느정도 해결할 수 있다

monitor안에는 하나의 process만 monitor 내부에 들어올 수 있다

monitor에 들어가있는 프로세스가 0개이거나

monitor 안에 있던 프로세스가 여러 사유에 의해 condition이 만족되지 않아 wait상태가 되면 그제서야 queue에 있던 프로세스들이 하나하나 들어올 수 있다.

image-20210425115147111

모니터는 프로그래밍 언어 차원에서 동기화 문제를 해결하는 high level 해결 방식이다.

OOP에서 객체를 기반으로 변수에 접근하는 것처럼

공유데이터는 외부에서 접근할 수 없고 monitor 안에서만 접근할 수 있도록 해둔 것인데 (private 함수로 monitor 내부에서만 관리)

프로그램 입장에서 이런 방식을 사용하면 Lock을 거는 작업을 프로그래머가 해줄 필요가 없다.

image-20210425115346199

monitor 방식에서 자원의 개수를 세는 작업은 condition 변수를 사용한다

condition 변수는 wait 연산과 signal 연산을 지원하는데

wait 함수는 프로세스를 잠들게 하는 역할을 한다 (x, y는 조건에 해당하며 x라는 조건을 만족하지 못한 경우 x의 queue에 가서 줄을 서게 된다.)

wait 상태에서 signal을 통해 깨워진다.

image-20210425115503129

monitor에서는 Lock을 걸고 푸는 복잡한 작업이 필요하지 않다 (공유 버퍼에 들어와서 코드를 실행하는 도중에 다른 프로세스의 접근은 모니터가 막아준다.)

둘 이상의 생산자나 소비자가 접근하는 문제가 발생하지 않는다.

condition 변수 full은 내용이 들어있는 버퍼를 기다리며 잠들게 하는 역할을 하고 empty는 내용이 들어있는 버퍼를 기다리며 잠들게 하는 역할을 한다.

image-20210425114448312

식사하는 철학자 문제를 Semaphore가 아닌 Monitor 방식으로 바꾸어보면 다음과 같은 코드를 작성할 수 있다.