1.03.2014

Getting Started with Erlang pt.4

Erlang をはじめよう その4

 

前回 - mog project: Getting Started with Erlang pt.3 の続き

CHAPTER 8: Playing with Processes

プロセスこそが Erlang のキー・コンセプトである。

プロセスIDの確認とメッセージの送受信
> self().
> self() ! test1.
> Pid = self().
> Pid ! test2.
> flush().
> flush().
> self() ! test1.
> receive X -> X end.
> self() ! 23.
> receive Y -> 2 * Y end.

 

モジュールからプロセスを生成する

このモジュールの場合、一度メッセージを受け取ったらプロセスは即座に終了する。

-module(bounce).
-export([report/0]).

report() ->
  receive
    X -> io:format("Received ~p~n", [X])
  end.

> c(bounce).
> Pid = spawn(bounce, report, []).
> Pid ! 23.
> Pid ! 45.

 

再帰を利用すれば、永久的にメッセージを受信できるようになる。

-module(bounce).
-export([report/0]).

report() ->
  receive
    X -> io:format("Received ~p~n", [X]),
    report()
  end.
> c(bounce).
> Pid = spawn(bounce, report, []).
> Pid ! 23.
> Pid ! message.

 

再帰のパラメータを変えることで、シンプルなカウンターを作れる。

-module(bounce).
-export([report/1]).

report(Count) ->
  receive
    X -> io:format("Received #~p: ~p~n", [Count, X]),
    report(Count + 1)
  end.
> c(bounce).
> Pid = spawn(bounce, report, [1]).
> Pid ! test.
> Pid ! 123.
> Pid ! message.

 

以下のように、receive の戻り値を利用してもよい。

-module(bounce).
-export([report/1]).

report(Count) ->
  NewCount = receive
    X -> io:format("Received #~p: ~p~n", [Count, X]),
    Count + 1
  end,
  report(NewCount).

 

プロセスの登録

引き続き同じ bounce.erl を使用。

> Pid1 = spawn(bounce, report, [1]).
> register(bounce, Pid1).
> regs().
> bounce ! hello.
> bounce ! 123.
> bounce2 ! test.    % error
> GetBounce = whereis(bounce).
> unregister(bounce).
> regs().
> whereis(bounce).
> GetBounce ! "Still there?".

 

プロセスが異常終了するとき
-module(fragile).
-export([report/0]).

report() ->
  receive
    X -> io:format("Divided to ~p~n", [X/2]),
    report()
  end.
> c(fragile).
> Pid = spawn(fragile, report, []).
> Pid ! 38.
> Pid ! 0.
> Pid ! one.
> Pid ! 10.

既に終了してしまったプロセスに対してメッセージを送っても、何も起こらない。

 

メッセージのコールバック
-module(shop).
-export([buy/0]).

buy() ->
  receive
    {From, Item, Number} ->
      From ! {Item, Number, price(Item, Number)},
      buy()
  end.

price(apple, Num)  when Num >= 0 -> 100 * Num;
price(banana, Num) when Num >= 0 -> 200 * Num;
price(_, Num)      when Num >= 0 -> 500 * Num.
price(_, _)                      -> 0.
> c(shop).
> P = spawn(shop, buy, []).
> P ! {self(), apple, 10}.
> P ! {self(), banana, 20}.
> flush().

 

関数の中でプロセスを生成する
-module(async_shop).
-export([async_shop/0]).

async_shop() ->
  Shop = spawn(shop, buy, []),
  buy(Shop).

buy(Shop) ->
  receive
    {Item, Number} ->
      Shop ! {self(), Item, Number},
      buy(Shop);
    {Item, Number, Price} ->
      io:format("You bought ~p ~p(s) for ~p Yen.~n", [Number, Item, Price]),
      buy(Shop)
  end.
> c(async_shop).
> P = spawn(async_shop, async_shop, []).
> P ! {apple, 10}.
> P ! {banana, 20}.

 

プロセスの状態を確認する
> pman:start().

GUI が起動する。特定のプロセスをダブルクリックすると、ヒープサイズなどの詳細な情報を得られる。
そのプロセスでメッセージの送受信が行われれば、その内容も表示される。

Pman Process 0 41 0 on nonode nohost

 

プロセスどうしのリンク
  • リンクしない場合
    > pman:start().
    > P = spawn(async_shop, async_shop, []).
    > P ! {apple, one}.

    子プロセス(shop)がエラーで終了した後も、親プロセス(async_shop)が残り続ける。

  • リンクした場合
    -module(async_shop).
    -export([async_shop/0]).
    
    async_shop() ->
      Shop = spawn_link(shop, buy, []),
      buy(Shop).
    
    buy(Shop) ->
      receive
        {Item, Number} ->
          Shop ! {self(), Item, Number},
          buy(Shop);
        {Item, Number, Price} ->
          io:format("You bought ~p ~p(s) for ~p Yen.~n", [Number, Item, Price]),
          buy(Shop)
      end.
    > c(async_shop).
    > pman:start().
    > P = spawn(async_shop, async_shop, []).
    > P ! {apple, one}.

    子プロセス(shop)がエラーで終了したら、親プロセス(async_shop)も終了する。 (リンクは常に双方向)

 

エラートラップと新しいプロセスの生成
-module(async_shop).
-export([async_shop/0]).

async_shop() ->
  process_flag(trap_exit, true),
  Shop = spawn_link(shop, buy, []),
  buy(Shop).

buy(Shop) ->
  receive
    {Item, Number} ->
      Shop ! {self(), Item, Number},
      buy(Shop);
    {'EXIT', Pid, Reason} ->
      io:format("FAILURE: ~p died because of ~p.~n", [Pid, Reason]),
      NewShop = spawn_link(shop, buy, []),
      buy(NewShop);
    {Item, Number, Price} ->
      io:format("You bought ~p ~p(s) for ~p Yen.~n", [Number, Item, Price]),
      buy(Shop)
  end.
> c(async_shop).
> pman:start().
> P = spawn(async_shop, async_shop, []).
> P ! {apple, one}.
> P ! {apple, 10}.
> P ! {banana, two}.

エラーをトラップしてメッセージが出力された後、即座に新しいプロセスが立ち上がる。
GUI では、エラーが発生するたびに shop:buy/0 の実行プロセスIDが新しくなっていくのを確認できる。

 

 

 

References

 

Related Posts

0 件のコメント:

コメントを投稿