Erlang World


top > concurrent programming > message

Message

プロセス間の通信

この章の最初に「Erlangのプロセス間に共有メモリはない」と学びました。 ある一つの変数を介して情報を受け渡すことができないのなら、複数のプロセスでどのように情報を受け渡せばいいのでしょうか。

実はErlangでプロセス間の通信を行なうためには「メッセージ(Message)」を利用します。 これはプロセスからプロセスに手紙を送るようなものだと言えます。 その手紙に送信者が伝えたいことを記して、受信者がそれを読み取るのですね。

メッセージを用いるのには、それを送信する方法と受信する方法が必要です。 この節ではそれを学びます。

メッセージの送信

メッセージの送信の仕方は次のようになっています。

プロセス識別子 ! メッセージ
Pid ! Message

! 」の前に送り先のプロセス識別子を書いて、 その後ろに送りたいメッセージを記すのです。

プロセス識別子(Process identifier)は略してPidと呼びます。
前の節で扱いましたが、これはspawn関数の返り値です。つまり、プロセスを作成した際にプロセス識別子を返すのです。

Pid = spawn(module,function,[Arg1,Arg2]).

spawn関数の返り値がプロセス識別子なので、Pidはプロセス識別子を格納します。

メッセージはErlangのタームだったら何でも大丈夫です。 通常はタプルに必要な値を格納して送信することが多いです。
これは受信の際のパターンマッチで確認します。

簡単な送信例を記します。

Pid ! {message, Message}.

メッセージの受信

次はメッセージの受信を行ないます。

自分宛に送信されたメッセージを受け取るには、決まった書式が必要です。

それは次のようになっています。

receive
    パターン1 ->
        処理1;
    パターン2 ->
        処理2;
    ............
    パターンN ->
        処理N
end.	

最後の処理Nの後に「 ; 」が無いのに注意して下さい。

それぞれのプロセスは自分独自のメールボックスを持っています。自分宛に届けられたメッセージは その受信ボックスに格納されるます。

receive文はメールボックスからメッセージを取り出す役割を持っています。

メッセージをメールボックスより取り出す際にはパターンマッチングが用いられます。

先に届いたメッセージから順番にパターンマッチングしていきます。 マッチするパターンが無い場合は、次のメッセージのチェックを行ない始めます。

メッセージ通信サンプル

それでは、サンプルを通して送受信を確認してみましょう

-module(hello_server).
-export([loop/0]).

loop() ->
    receive
        {hello, X} ->
            io:format("hello:~p~n",[X]),
            loop();
            
        {erlang, X, Y} ->
            io:format("erlang:~p  :~p~n",[X,Y]),
            loop();
            
        Other ->
            io:format("not supported:~p~n",[Other]),
            loop()
    end.

loop/0はメッセージを受信する関数です。 再帰関数になっているのが分かります。

実行してみましょう。

1> Pid = spawn(hello_server, loop, []).
<0.33.0>

2> Pid ! {hello, yuichi}.
hello:yuichi
{hello,yuichi}

実行結果の1行目はプロセスを作っています。そしてプロセス識別子を得ています。

次に、そのプロセス識別子に向かって {hello, yuichi} というメッセージを送っています。

宛先となっている受信プロセスでは、受信処理がなされています。
パターンマッチングで {hello, X} が適合するので、その処理が行なわれています。

他のサンプルも見てみましょう。

3> Pid ! {erlang, "Ericsson's","Language"}.
erlang:"Ericsson's"  :"Language"
{erlang,"Ericsson's","Language"}

4> Pid ! {erlang, ericsson}.
not supported:{erlang,ericsson}
{erlang,ericsson}

5> Pid ! {hello, yuichi, ito}.
not supported:{hello,yuichi,ito}
{hello,yuichi,ito}

6> X = {hello, world}.
{hello,world}
7> Pid ! X.
hello:world
{hello,world}

受信プロセスの最後にある Other はどんなメッセージでも表示します。 上のパターンマッチに適合しなかった全てのメッセージはこれに引っかかるのです。

after

最後にメッセージのタイムアウトについて扱います。

これは簡単に言うと、メッセージが届くまでどれぐらい待つのかを指定するものです。書式を見てみましょう

receive
    パターン1 ガード2 ->
        処理1;	
    パターン1 ->
        処理2;
    パターン2 ガード->
        処理3;
        
after 
    Time ->
        Timeミリ秒メッセージが来なかった時の処理
end,

前述しなかったのですが、受信処理においてもガードを使うことは可能です。

after節以降がタイムアウト処理になっています。

サンプルを確認してみましょう。

-module(alarm_clock).
-export([start/1]).

start(Time) ->
    clock(Time, 0).
    
clock(Time, X) ->
    receive
        stop ->
            io:format("stop clock~n",[]),
            X;
            
        {change, NewTime} ->
            io:format("change span ~p to ~p~n",[Time,NewTime]),
            clock(NewTime, X);
            
        Other ->
            io:format("not supported:~p~n",[Other]),
            clock(Time, X)
            
    after 
        Time ->
            io:format("~p mseconds~n",[X + Time]),
            clock(Time, X + Time)
    end.

実行してみます。

9> Pid = spawn(alarm_clock,start,[3000]).
<0.43.0>
3000 mseconds                               
6000 mseconds             
10> Pid ! {change,5000}.
change span 3000 to 5000
{change,5000}
11000 mseconds                              
16000 mseconds                              
11> Pid ! hello.
not supported:hello
hello
21000 mseconds
26000 mseconds
12> Pid ! stop.
stop clock
stop

特に難しいところはないと思うのですけれど、最後に注目して下さい。

Pid ! stop. の返り値が stop になっていますね。 これは最後に実行された文が自分のプロセスだからです。相手先のプロセスで行なわれた処理は返り値となっていないのです。

次の節では、この節を発展させてプロセスの返信方法を扱います。 新しい技術ではなく、定石と呼べるようなものなので 覚えておくと便利だと思います。

BIF

関連するBIFを記します。まだ学んでいない内容については後ほど扱います。

プロセスの生成

spawn(Fun)
新しいプロセスをFun/0から作成し、その識別子を返す。

spawn(Node, Fun)
ノードNodeに新しいプロセスをFun/0から作成し、その識別子を返す。

spawn(Module, Function, Args)
モジュールModuleの関数Function(Args)を新しいプロセスとしてスタートさせて、その識別子を返す。

spawn(Node, Module, Function, ArgumentList)
ノードNodeにモジュールModuleの関数Function(Args)を新しいプロセスとしてスタートさせて、その識別子を返す。

プロセスの情報

self()
関数を呼び出したプロセス自身のPidを返す。

is_pid(Term)
タームTermがもしプロセスIDであればture、そうでなければfalseを返します。 

processes()
自ノードで現在作動中のプロセスをリストで返す。

is_process_alive(Pid)
プロセス識別子Pidがもし生きているのであればture、そうでなければfalseを返します。

メッセージの送信

erlang:send(Dest, Msg)
メッセージを送信する。 Dest ! Msg. と同じ意味である。返り値はMsg。

erlang:send_after(Time, Dest, Msg)
Timeミリ秒後にメッセージをDestに向かって送信する。

型変換

pid_to_list(Pid)
Pidを文字列に変換する。デバッグ以外では非推奨。

list_to_pid(String)
文字列をPidに変換する。デバッグ以外では非推奨。

Yuichi ITO. All rights reserved.
mail to : ad
inserted by FC2 system