Languages/Erlang2008.09.22 17:19

앞선 글을 통해 Erlang에서 병렬 프로그래밍이 어떻게 이루어지는지 대략이나마 감을 잡으실 수 있었을 거라고 믿습니다. 다시 정리해보자면, 얼랭에서 병렬 프로그램은 프로세스의 집합으로 만들어지고, 프로세스 끼리 메시지를 주고받아 통신하는 방식을 사용해 구현됩니다. 메시지를 보낼때는 Pid ! Message 이런 형식으로 보내고, 받는 쪽에서는 receive 를 사용해 메시지를 받아 처리합니다.

그러면 지금부터는 앞선 글에서 만든 프로그램을 진짜 클라이언트/서버 모델을 만족하도록 바꿔 보겠습니다. 클라이언트가 서버에게 요청을 보내면, 서버는 그 요청을 해석해서 클라이언트에게 답을 보내주어야 합니다. 다음의 코드를 보시죠.

-module(concur).
-export([start_server/0, send_request/2, send_stop/1]).

start_server() -> spawn( fun loop/0 ).

loop() ->
    receive
        {request, Pid, L} ->
            Result = odds_and_evens_acc(L),
            Pid ! {response, self(), Result},
            loop();
        stop ->
            exit
    end.

send_request(Pid, L) ->
    Pid ! {request, self(), L},
    receive
        {response, _SPid, {{odd, Ov},{even,Ev}}} ->
            io:format("odd: ~p, even: ~p~n", [Ov,Ev])
    end.

send_stop(Pid) -> Pid ! stop.

odds_and_evens_acc(L) ->
    odds_and_evens_acc(L, 0, 0).

odds_and_evens_acc([H|T], Odds, Evens) ->
    case ( H rem 2 ) of
        1 -> odds_and_evens_acc(T, Odds + H, Evens);
        0 -> odds_and_evens_acc(T, Odds, Evens + H)
    end;
odds_and_evens_acc([], Odds, Evens) ->
    { {odd, Odds}, {even, Evens} }.

클라이언트/서버 모델을 따르는 통신을 하기 위해, 서버에게 보내는 메시지의 형식이 좀 바뀌었습니다. 메시지가 투플 형태로 간다는 것은 종전과 똑같습니다만, 그 투플의 두 번째 항목에 요청을 보내는 클라이언트 프로세스의 PID가 들어간다는 점이 달라졌습니다. 이 PID가 있어야 서버는 어느 클라이언트에게 응답을 보내야 할 지를 알 수  있죠. 서버는 요청이 오면 odds_and_evens_acc를 호출해서 결과를 계산한 다음에, 요청을 보낸 프로세스에게 응답을 전송합니다. 전송되는 응답 투플 맨 앞에는 '응답'임을 알리기 위해 response라는 아톰이 들어가고, 투플 두번째 항목에는 서버 프로세스의 PID가 들어가고(self()를 호출하면 자기 자신의 PID를 얻을 수 있습니다) 세 번째 항목에는 보낼 계산 결과값이 들어갑니다. 

클라이언트는 서버에게 요청을 보내기 위해 send_request 함수를 이용해야 합니다. 이 함수의 첫번째 인자로는 서버의 PID가 오고, 두 번째 인자로는 리스트가 전달됩니다. send_request는 해당 인자들을 사용해 서버에 메시지를 전송한 다음, 스스로 receive를 통해 그 서버가 보내는 응답을 전송받습니다. 그런 다음 응답을 해석해 화면에 결과값을 뿌리죠. (서버가 보내는 PID는 사용하지 않을 것이기 때문에 _SPid와 같은 이름의 변수를 썼다는 것에 주의합시다. _가 앞에 붙는 변수는 설사 이후의 코드에서 사용되지 않아도 컴파일러가 ok합니다.)

위의 코드는 다음과 같이 사용합니다.

14> c(concur).
{ok,concur}
15> Pid = concur:start_server().
<0.90.0>
16> concur:send_request(Pid, [1,2,3,4,5,6]).
odd: 9, even: 12
ok
17>

이렇게 해서 클라이언트/서버 모델을 따르는 간단한 예제까지 만들어 봤습니다. 이해하기 쉬우셨는지 모르겠군요. 다음 글에서는 실제로 메시지 송수신 과정에서 발생할 수 있는 문제에 대처할 수 있는 코드를 만드는 방법을 포함해, 조금 더 심화된 주제들을 살펴보겠습니다.

머리가 터질거같은 상태에서 억지로 글을 쓰다보니 더 이상 길게는 못쓰겠군요. 머리 터질때 코드 작성하는 일 말고는 할 수 있는게 없는 저 자신도 참 한심하기 짝이 없습니다만....




신고
Posted by 이병준

소중한 의견, 감사합니다. ^^