This is a Korean translation of Rholang vs. the Dining Philosophers.

 

Rholang 은 동시적 프로그래밍 언어로 자주 장점이 내세워집니다. 속도가 놀라울 정도로 빠른 것이, 꼭 필요할 시에만 순차적인 처리를 하기 때문입니다. 이는 resource starvation 또는 thread deadlock 과 같은 문제를 피해갈 수 있게 해줍니다. 그리고, 모든 것이 동시적 컴퓨터 모델에 내장되어 있기 때문에, 투박하거나 사용하는데 어려움이 없습니다.

 

이때까지 접한 프로그래밍 언어 중 단연 가장 섹시합니다만, 이 모든 것이 무슨 의미를 가질까요? Rholang의 실제 사용 혜택, 내재된 동시성을 컴퓨터과학 문제로 일컬어지는 ‘철학자들의 만찬’ 문제의 맥락에서 살펴보도록 하겠습니다.

 

문제 상황

 

두명의 철학자가 한 탁상에서 서로 동과 서쪽으로 마주보며 앉아있다고 상상해보십시오. 둘 다 파스타가 담긴 접시가 있지만, 한 쌍의 식기밖에 없습니다. 칼은 북쪽에 놓여있고, 포크는 남쪽에 놓여있습니다. 모든 사람들이 파스타를 먹는데 두 종류의 식기가 모두 필요하다는 것을 알고 있습니다. 따라서어느 철학자도 두 쪽의 식기가 있기 전까지 파스타를 먹지 못합니다. 가장 고전적 문제는 철학자들이 식기류를 기분좋게 나누어 저녁을 먹을 수 있도록행동 수행 원칙을 규정하는 것입니다.

 

해결 방안

 

자연스러운 첫 번째 접근은 철학자들이 아래의 알고리즘을 따르도록 하는 것입니다.

 

좌측의 식기를 기다린다
우측의 식기를 기다린다
1분 동안 먹는다
식기를 둘 다 제자리에 놓는다
1분 동안 생각한다

 

완벽하게 순차적인 환경에서 구동된다면, 이 프래그램은 합리적으로 잘 운영됩니다. 첫번째 철학자가 명령을 따르고, 1분동안 먹고, 두번째가 반복하고,각자가 균일하게 턴을 가지며 먹을 것입니다. 실제로는, 각 철학자는 독립적 행동자이며 동시적으로 행동할 수가 없습니다. 이 현실 상황은 병렬성이 허락된다면 만드는것이 가능합니다. 새로운 상황은 이렇습니다:

 

각 철학자는 이렇게 행동합니다:

 

 좌측의 식기를 집는다
 우측의 식기를 집는다
1분동안 먹는다
두 식기를 모두 제자리에 놓는다
1분동안 생각한다

 

우리가 두 철학자에게 동시적으로 행동하라고 하자마자 문제에 직면합니다. 예를 들면, 두 철학자가 좌측의 식기를 동시에 잡을 수도 있고, 영원히 우측의 식기를 집기까지 기다릴 수 있습니다. 이 데드락에서, 철학자들은 말그대로 굶음(starvation) 문제를 경험하게 됩니다 ☺.
더 좋은 알고리즘은 두 철학자가 동시에 행동하더라도 저녁을 먹을 수 있게 허락해주는 것입니다:

 

각 철학자는 이렇게 행동합니다:

 

식기 둘 다 사용 중이지 않을 때까지 기다린다
두 식기를 둘다 집는다
1분동안 먹는다
두 식기를 반납한다
1분동안 생각한다

 

파이썬Python 에서의 예시

 

원 알고리즘은 순차적 프로그래밍 언어인 파이썬처럼 상대적으로 코딩하기 수월합니다. 그것이 언어로 표현하기에 가장 자연스러운 해결방안입니다.

 

from threading import Thread, Lock
# Set the table
fork = Lock()
knife = Lock()
# Philosopher 1’s plan
phil1 = Thread(target=plan1)
def plan1():
  fork.acquire(True)
  knife.acquire(True)
  print(“Philosopher 1 is full.”)
  fork.release()
  knife.release()

 

# Likewise for philosopher 2
phil2 = Thread(target=plan2)
def plan2():
  knife.acquire(True)
  fork.acquire(True)
  print(“Philosopher 1 is full.”)
  knife.release()
  fork.release()

 

# Hard to give them a fair start in a sequential language
phil1.start()
phil2.start()

 

비록 우리가 스레딩을 따로 가져와야하지만, 이 코드는 이해하기 어렵지 않습니다. 특히 우리가 순차적 언어를 비판하는데 사용한 시간까지 감안한다면요. 하지만, 이 코드는 위에서 언급된 치명적 오류가 존재합니다. 두 가지의 별개 스레드 위에 구동된다면, 철학자1이 포크를 집을 것이고, 철학자2가 나이프를 집고, 둘 다 굶을 것입니다. 아래의 파이썬 코드는 더 나은 두 번째 알고리즘을 실행시킵니다.

 

from threading import Thread, Lock
# Set the table
fork = Lock()
knife = Lock()
# Philosopher 1’s plan
phil1 = Thread(target=plan1)
def plan1():
 while True:
   if fork.acquire(False):
     if knife.acquire(False):
       print(“Philosopher 1 is full.”)
       knife.release()
     fork.release()
# Likewise for philosopher 2
phil2 = Thread(target=plan2)
def plan2():
 while True:
   if knife.acquire(False):
     if fork.acquire(False):
       print(“Philosopher 1 is full.”)
       fork.release()
     knife.release()
# Hard to give them a fair start in a sequential language
phil1.start()
phil2.start()

 

이 코드는 데드락 문제를 해결하진 않지만, 시각적으로 더 복잡하고, 더 깊이 내재되어 있으며, 프로그래머로부터 의식적으로 리소스 락에 대해 인지하고 있도록 요구합니다. 파이썬에서는, 어떠한 순차적 언어처럼, 멀티 스레딩과 복잡성을 교환해야합니다. 따라서, 프로그래머들은 스레드를 안전하지 않은 방법으로 프로그래밍하는 것을 먼저 배웁니다. 역시 순차적 프로그래밍의 세계에서는 멀티 스레딩은 상급 개발자에게 주어지는 과제입니다.

 

RHOLANG에서의 예

 

이 Rholang 예시들은 Mike Stay로부터 원래 쓰여졌고, 이 글을 위해 살짝 개작되었습니다.
첫 번째 예시에서, 두 철학자들은 첫 번째 알고리즘을 따르고 데드락 상황을 전처럼 맞게 됩니다. 철학자들의 계획은 두 레벨 더 들여써져 있습니다, 왜냐하면 철학자가 먼저 첫번째 식기를 기다리고 있고, 그리고 두 번째 식기를 기다리고 있기 때문입니다.

 

new log(`rho:io:stdout`), north, south, knife, spoon in {
  // Set the table
  north!(*knife) |
  south!(*spoon) |
  // Philosopher 1’s plan
  for (@knf <- north) {
    for (@spn <- south) {
      log!(“Philosopher 1 is full.”) |
      north!(knf) |
      south!(spn)
    }
  } |
  // Likewise for philosopher 2
  for (@spn <- south) {
    for (@knf <- north) {
      log!(“Philosopher 2 is full.”) |
      north!(knf) |
      south!(spn)
    }
  }
}
두 번째 버전은 첫 번째와 거의 유사합니다. 하지만, 식기류를 기다리는 과정은 ; join operator 를 통해 하나로 결합되었습니다. 이제 각 철학자는 항상두 식기류가 동시에 모두 있을 때까지 기다릴 것입니다. Rholang의 join operator에 대해서 더 알고 싶다면, 튜토리얼 내의 레슨을 확인해주십시오.

 

new log(`rho:io:stdout`), north, south, knife, spoon in {
  // Set the table
  north!(*knife) |
  south!(*spoon) |
  // Philosopher 1’s plan
  for (@knf <- north; @spn <- south) {
    log!(“Philosopher 1 is full.”) |
    north!(knf) |
    south!(spn)
  } |
  // Likewise for philosopher 2
  for (@spn <- south; @knf <- north) {
    log!(“Philosopher 2 is full.”) |
    north!(knf) |
    south!(spn)
  }
}
파이썬 예시와 다르게, Rholang에서는 코드가 오히려 더 단순해졌습니다. 우리가 동시성- 친화적 버전을 실행했을 때, 프로그래머가 신경써야할 요소가 줄어들어듭니다. Rholang은 완벽하게 동시적 연산 모델을 사용하기 때문에, 프로그램을 가장 자연스럽게 표현할 표현은 ‘올바른’ 방향입니다. 순차적 프로그래머가 Deep nesting과 Explicit lock을 처리해야 할 때, 이런 디테일들은 Rholang의 내부에 숨겨져있습니다. 따라서 프로그래머의 짐이덜어지게 됩니다.

 

반영 REFLECTIONS

 

우리는 Rholang의 Syntax가 자연스럽게 완전한 Deadlock을 피하여 모든 철학자들이 저녁을 먹게끔해준다는 것을 보여주었습니다. Rholang 의 프로그래머들은 동시적 프로그래밍을 통한 자본화를 위해 추가적 복잡성 때문에 고통받지 않아도 됩니다. 오히려, 가장 자연스럽게 프로그램을 표현하는방법은 안전한-스레드 방식입니다. 순차적 코드를 의도적으로 작성할 때만 Rholang에서 Deadlock이 발생할 수 있습니다. 저는 철학자들이 하나의 그릇을 가지고 경쟁을 한다면 어떤 결과가 나올지 가끔 생각해보았습니다. 이제는 Rholang 프로그래머로서, 모든 멀티스레딩과 데드락에 사용되던 뇌-에너지를 이런 새로운 문제를 위해 사용할 수 있게 되었습니다.