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: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
0 件のコメント:
コメントを投稿