July 04, 2022
이전 글에서는 발생할 수 있는 문제에 대해 많이 언급했습니다.
또한 임계구역 문제, 경쟁조건이 없는 프로그램을 설계할 때 발생하는 문제, 교착상태, 라이브니스 위험 등을 언급했습니다.
이 글에서는 제시된 도구를 고전적인 동기화 문제에 적용합니다.
먼저 유한 버퍼 문제가 존재합니다.
유한한 개수의 item을 임시로 보관하는 버퍼에 여러 명의 생산자들과 소비자들이 접근한다. 생산자는 item이 하나 만들어지면 그 공간에 저장한다. 이때 저장할 공간이 없는 문제가 발생할 수 있다. 소비자는 item이 필요할 때 버퍼에서 item을 하나 가져온다. 이 때는 소비할 item이 없는 문제가 발생할 수 있다.
이 문제를 해결하는 것을 생산자-소비자 협동이라 하며 버퍼가 동기화되어 정상적으로 동작하는 상태를 뜻합니다.
// n개의 버퍼로 구성된 pool이 존재하며 각 버퍼는 한 item을 저장
val n: Int
var mutex: Semaphore = 1 // 상호 배제 기능을 제공
var empty: Semaphore = n // 비어있는 버퍼 수
var full: Semaphore = 0 // 사용중인 버퍼 수
//================================================
// 생산자
while (true) {
// produce
wait(empty)
wait(mutex)
// next produce
signal(mutex)
signal(full)
}
//================================================
// 소비자
while (true) {
wait(full)
wait(mutex)
// remove item
signal(mutex)
signal(empty)
//consume
}
병행 프로세스들이 한 DB를 공유한다면 배타적 접근 권한이 필요합니다.
해당 문제는 두 가지모드를 지원하는 락을 둬서 해결합니다.
다음과 같은 상황에서 유용합니다.
Wirter보다 reader의 개수가 많은 응용
이전 글에서 언급한 식사하는 철학자 문제의 해결안을 살펴봅니다.
1. 일정 시간 생각을 한다.
2. 왼쪽 포크가 사용 가능해질 때까지 대기한다. 만약 사용 가능하다면 집어든다.
3. 오른쪽 포크가 사용 가능해질 때까지 대기한다. 만약 사용 가능하다면 집어든다.
4. 양쪽의 포크를 잡으면 일정 시간만큼 식사를 한다.
5. 오른쪽 포크를 내려놓는다.
6. 왼쪽 포크를 내려놓는다.
7. 다시 1번으로 돌아간다
두 포크를 모두 집을 수 있는지 판단한 뒤에 집습니다.
비대칭 해결안을 활용합니다.
우선순위로 해결합니다.
시간제한으로 해결합니다.
해당 해결안들은 효율성이 고려되지 않았습니다.
데드락이 없는 해결안이 반드시 기아의 가능성도 제거하지는 않습니다.
앞서 언급한 식사하는 철학자의 해결안을 제시하며 모니터의 개념을 설명합니다.
양쪽 포크를 모두 집을 수 있을 때에만 포크를 집을 수 있도록 강제합니다.
enum class State {
THINKING,
HUNGRY,
EATING
}
class DiningPhilosophers : Monitor {
val state = mutableListOf<State>()
// 원하는 포크를 집을 수 없을 때 집기를 미룰 수 있게 한다.
val self = mutableListOf<Condition>()
init {
(0..5).forEach { i ->
state.append(THINKING)
}
}
// 식사 전
fun pickUp(i: Int) {
state[i] = HUNGRY
eat(i)
if (state[i] != EATING) self[i].wait()
}
// 식사 후
fun putDown(i: Int) {
state[i] = THINKING
eat(left(i))
eat(right(i))
}
private fun left(i: Int) = (i + 4) % 5
private fun right(i: Int) = (i + 1) % 5
fun eat(i: Int) {
if (canEat(i)) {
state[i] = EATING
self[i].signal()
}
}
private fun canEat(i: Int) = state[left(i)] != EATING &&
state[i] == HUNGRY &&
state[right(i)] != EATING
}
데드락을 해결한 코드이지만 기아 상태를 해결하진 못했습니다.