Erlang をはじめよう その4
前回 - mog project: Getting Started with Erlang pt.3 の続き
CHAPTER 8: Playing with Processes
プロセスこそが Erlang のキー・コンセプトである。
プロセスIDの確認とメッセージの送受信
1 2 3 4 5 6 7 8 9 10 | > self(). > self() ! test1. > Pid = self(). > Pid ! test2. > flush(). > flush(). > self() ! test1. > receive X -> X end. > self() ! 23. > receive Y -> 2 * Y end. |
モジュールからプロセスを生成する
このモジュールの場合、一度メッセージを受け取ったらプロセスは即座に終了する。
1 2 3 4 5 6 7 | -module(bounce). -export([report/0]). report() -> receive X -> io:format("Received ~p~n", [X]) end. |
1 2 3 4 | > c(bounce). > Pid = spawn(bounce, report, []). > Pid ! 23. > Pid ! 45. |
再帰を利用すれば、永久的にメッセージを受信できるようになる。
1 2 3 4 5 6 7 8 | -module(bounce). -export([report/0]). report() -> receive X -> io:format("Received ~p~n", [X]), report() end. |
1 2 3 4 | > c(bounce). > Pid = spawn(bounce, report, []). > Pid ! 23. > Pid ! message. |
再帰のパラメータを変えることで、シンプルなカウンターを作れる。
1 2 3 4 5 6 7 8 | -module(bounce). -export([report/1]). report(Count) -> receive X -> io:format("Received #~p: ~p~n", [Count, X]), report(Count + 1) end. |
1 2 3 4 5 | > c(bounce). > Pid = spawn(bounce, report, [1]). > Pid ! test. > Pid ! 123. > Pid ! message. |
以下のように、receive の戻り値を利用してもよい。
1 2 3 4 5 6 7 8 9 | -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 を使用。
1 2 3 4 5 6 7 8 9 10 11 | > 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?". |
プロセスが異常終了するとき
1 2 3 4 5 6 7 8 | -module(fragile). -export([report/0]). report() -> receive X -> io:format("Divided to ~p~n", [X/2]), report() end. |
1 2 3 4 5 6 | > c(fragile). > Pid = spawn(fragile, report, []). > Pid ! 38. > Pid ! 0. > Pid ! one. > Pid ! 10. |
既に終了してしまったプロセスに対してメッセージを送っても、何も起こらない。
メッセージのコールバック
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | -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. |
1 2 3 4 5 | > c(shop). > P = spawn(shop, buy, []). > P ! {self(), apple, 10}. > P ! {self(), banana, 20}. > flush(). |
関数の中でプロセスを生成する
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | -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. |
1 2 3 4 | > c(async_shop). > P = spawn(async_shop, async_shop, []). > P ! {apple, 10}. > P ! {banana, 20}. |
プロセスの状態を確認する
1 | > pman:start(). |
GUI が起動する。特定のプロセスをダブルクリックすると、ヒープサイズなどの詳細な情報を得られる。
そのプロセスでメッセージの送受信が行われれば、その内容も表示される。
プロセスどうしのリンク
- リンクしない場合
123> pman:start().
> P = spawn(async_shop, async_shop, []).
> P ! {apple, one}.
子プロセス(shop)がエラーで終了した後も、親プロセス(async_shop)が残り続ける。
- リンクした場合
async_shop.erl 12345678910111213141516-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.
1234> c(async_shop).
> pman:start().
> P = spawn(async_shop, async_shop, []).
> P ! {apple, one}.
子プロセス(shop)がエラーで終了したら、親プロセス(async_shop)も終了する。 (リンクは常に双方向)
エラートラップと新しいプロセスの生成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | -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. |
1 2 3 4 5 6 | > 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 件のコメント:
コメントを投稿