Process 생성하기
앞글에도 적었지만, Elixir 의 Process는 주요 특성이 있습니다.
- Process는 독립적으로 동시에 실행된다. 한 Process의 종료가 다른 Process에 영향을 미치지 않는다. (Link 라는 개념으로 영향을 줄수도 있는데, 이는 다음에 적겠습니다.)
- Process 간에 공유되는 것은 없으며, Process간의 협업은 Message 전달로 이루어진다. (함수 호출이 아닌, 메세지 전달)
이들에 대해서 하나씩, 상세히 적어보겠습니다.
우선, 가장 쉽게 Process를 느껴보도록 하죠..
iex 를 실행시킨 후, 아래와 같이 self() 명령을 치면,
#PID<0.106.0> 과 같은 Process ID 가 나옵니다.
이 PID 는 iex 자체의 Process 입니다. 즉 , iex 도 Elixir Process 로 실행이되는 것입니다.
PID 는 실행시키는 PC 마다 환경에 따라 다른 값이 나올 수 있습니다.
그 다음에는 Process에게 Message를 보내보죠.
아래와 같이 send 함수를 이용해서 메세지를 보낼수 있으며, 보내는 값은 어떤 값이라도 괜찮습니다.
send( self(), {:hello, "Process", %{a: 1, b: 2}} )
화면에 나타난 값 "{:hello, "Process", %{a: 1, b: 2}}" 은 send 함수의 return 값이 출력된것이지,
수신된 message 자체를 출력한것은 아닙니다. iex 에서 어떤 함수를 호출하면, 그 함수의 return 값이 출력되는 것 처럼. send 함수의 return 값이 화면에 출력된 것 입니다. send 함수는 어떤 process에게 전송된 메세지를 반환하게 되며, 그 메세지가 화면에
출력 된것 입니다.
위의 명령은 메세지를 받는 대상이 self() 이므로, 자기 자신에게 메세지를 보낸 것입니다.
수신된 메세지는 Process의 Message Mailbox 에 저장되게 됩니다.
프로그램을 개발할때 각 Process에 수신된 메세지는 receive 함수로 읽게 됩니다. 이의 사용은 잠시후 보겠습니다.
iex 에서는 flush() 명령으로, 수신된 메세지가 있으면 이를 읽어와서 화면에 표시하게 됩니다.
첫번째, flush() 명령에서는 수신된 메세지인 "{:hello, "Process", %{a: 1, b: 2}}" 를 출력합니다.
두번째 flush() 에서는 이미 메세지를 읽어왔기 때문에 아무런 값이 출력되지 않습니다.
Process간의 통신(협업)은 모두 메세지를 보내는 send 함수와 메세지를 수신하는 receive 함수(iex 에서는 flush) 로 이루어집니다.
이제, Process 생성에 대해 알아보겠습니다.
Process의 생성은 spawn 과 spawn_link 함수로 합니다.
우선은 spawn 함수보터 알아 보겠습니다.
우선 아주 간단히, 자신의 pid 를 출력하는 Process를 만들어 보겠습니다.
IO.inspect(self())
이 코드를 실행하면, 위와 같이 iex 창에서 self() 를 실행한것 같이 iex 자체의 pid 를 출력합니다.
spawn(fn -> IO.inspect(self()) end)
동일한 code 를 spawn 을 통해서 실행을 시켰더니, 다른 PID 값이 출력되었습니다.
즉, 이는 spawn 을 통해서 새로운 Process를 생성하고, 이 Process가 self() 를 실행하니, 새로 생성된 Process의 ID 가 출력 된것 입니다.
이와 같이, Elixir 는 spawn 함수를 이용하여 Process 를 만들수 있습니다.
위의 코드에서, IO.inspect(self()) 를 출력한 Process는 더이상 실행할 code 가 없으므로, 종료가 된 상태 입니다.
spawn에 대해서 좀더 알아보기 위해 , 계산에 오래 걸리는 함수를 하나 만들어 보겠습니다.
다음은, 원주율 Pi 를 계산하는 코드 입니다. (참조 : https://ko.wikihow.com/Pi-%EA%B3%84%EC%82%B0%ED%95%98%EB%8A%94-%EB%B2%95 )
1..10_000_000//2 |> Enum.reduce({0,1},fn i,{pi,mul} -> {pi + (4/i) *mul , mul * -1} end)
(1..10_000_000//2 에서 //2는 최신 Elixir version에서 지원하는 step 입니다. - elixir v1.12.0 부터 step 이 지원됩니다. 이 값은 1, 3, 5, 7 ,.. 의 수열을 만듭니다.)
컴퓨터의 성능에 따라 다르지만, 이 코드를 실행시키면 대략 10초 정도의 시간이 소요됩니다. 코드 입력후, Enter 키를 치면.
대략 10초 동안 iex 가 반응이 없게됩니다. 즉, iex process가 계산을 하고 , 계산이 완료된 후 결과를 출력하는 것 입니다.
동일 코드를 process를 생성해서 실행해보겠습니다.
spawn(fn -> 1..10_000_000//2 |> Enum.reduce({0,1},fn i,{pi,mul} -> {pi + (4/i) *mul , mul * -1} end) |> IO.inspect() end)
코드를 입력하고 Enter를 치면, 아까와는 다르게, iex 는 바로 반응을 하고
대략 10초 전후에 계산 결과가 화면에 출력 됩니다.
코드를 실행시킨 후, 바로 출력된 PID 값은 위의 코드에서와 같이 새로 생성한 Process의 ID 입니다.
즉 시간이 걸리는 연산처리는 새로 생성된 프로세스에서 실행을 했으니, iex 는 바로 응답을 할 수 있는것이고
생성된 프로세스가 연산을 마친 후, 계산 결과를 화면에 출력한 것 입니다.
이와 같이 Elixir 에서는 Process를 생성하여, 어떤 일을 시킬 수 있습니다.
이번 장에서는 우선 Process의 생성만 다루고, 다음장에서는 Process간의 통신을 다루겠습니다.