上記のコードのregister/2
が思った通りに動かない。
エラーは吐かないがwhereis/0
でundefined
が返ってくる。
undefined
が返ってくる理由は、register/2
で登録したプロセスがwhereis/0
を呼んだときにすでに終了しているからです。
Erlangのプロセスはspawn/2
で起動し、引数として渡された関数を実行します。そして関数から戻るとプロセスも終了します。ご質問のコードではspawn/2
にfun() -> io:format("SLEEP!~n") end
という関数を渡しています。この関数はターミナルSLEEP!と表示したらすぐに戻りますので、せっかく起動したプロセスもすぐに終了してしまいます。
同じような関数で少し実験してみましょう。以下のようなプログラムを書いてみます。
examples.erl
erlang
1-module(examples).
2-export([hello/0]).
3
4hello() ->
5 io:format("hello[~p]: Hello!~n", [self()]).
examples
モジュールのhello
関数はターミナルに自分のPIDとHello!という文字列を表示してすぐに戻ります。これをspawn/3
で実行してみましょう。
erl
1$ erl
2
3> c("examples").
4{ok,examples}
5
6> Pid1 = spawn(examples, hello, []).
7hello[<0.91.0>]: Hello!
8<0.91.0>
9
10%% erlang:is_process_alive/1は与えられたPIDを持つプロセスが
11%% 存在するならtrueを返す
12> is_process_alive(Pid1).
13false
このようにPIDが<0.91.0>
のプロセスが起動したものの、is_process_alive/1
を実行したときにはすでに終了してしまっていることがわかります。
うまく動かすには関数がすぐに終了しないように関数の実行を途中で止めなければいけません。例えば以下のecho
のようにreceive
を呼ぶと、何かメッセージを受信するまでその場で待たされますので、とりあえずはうまくいきます。
erlang
1-export([hello/0, echo/0, rpc/2]).
2
3%% この関数は受信したメッセージを送信者に送り返す。メッセージを1つ受信したら関数から戻る
4echo() ->
5 receive
6 %% 送信者へメッセージを送り返す
7 {Sender, Msg} ->
8 io:format("echo[~p]: Received ~p from ~p~n", [self(), Msg, Sender]),
9 Sender ! {self(), Msg}
10 end.
11
12rpc(Pid, Msg) ->
13 Pid ! {self(), Msg},
14 io:format("rpc[~p]: Sent ~p to ~p~n", [self(), Msg, Pid]),
15 receive
16 {Pid, Msg1} ->
17 io:format("rpc[~p]: Received ~p from ~p~n", [self(), Msg1, Pid])
18 after 5000 ->
19 io:format("Time out!~n")
20 end.
実行してみます。
erl
1> Pid2 = spawn(examples, echo, []).
2<0.94.0>
3
4> is_process_alive(Pid2).
5true
6
7%% メッセージを送信する
8> examples:rpc(Pid2, "Hey!").
9rpc[<0.84.0>]: Sent "Hey!" to <0.94.0>
10echo[<0.94.0>]: Received "Hey!" from <0.84.0>
11rpc[<0.84.0>]: Received "Hey!" from <0.94.0>
12ok
13
14%% receiveを実行したあとに関数から戻るのでプロセスが終了する
15> is_process_alive(Pid2).
16false
このように1回だけメッセージが受信できるようになりました。
もしプロセスにいくつもメッセージを受信させたいなら、関数から戻らないように関数内で自分自身を呼び出す必要があります。
erlang
1-export([hello/0, echo/0, echo_loop/0, rpc/2]).
2
3%% この関数は受信したメッセージを送信者に送り返す
4%% quitというメッセージを受信しない限りは、自分自身を呼び出すことで同じ処理を繰り返す
5echo_loop() ->
6 receive
7 %% quitを受信したら終了する
8 {Sender, quit} ->
9 io:format("echo_loop[~p]: Received quit from ~p~n", [self(), Sender]),
10 Sender ! {self(), "Bye."};
11 %% それ以外のメッセージは送信者へ送り返す
12 {Sender, Msg} ->
13 io:format("echo_loop[~p]: Received ~p from ~p~n", [self(), Msg, Sender]),
14 Sender ! {self(), Msg},
15 %% 終了しないように自分自身を呼び出す
16 echo_loop()
17 end.
erl
1> Pid3 = spawn(examples, echo_loop, []).
2<0.104.0>
3
4> examples:rpc(Pid3, "Howdy!").
5rpc[<0.102.0>]: Sent "Howdy!" to <0.104.0>
6echo_loop[<0.104.0>]: Received "Howdy!" from <0.102.0>
7rpc[<0.102.0>]: Received "Howdy!" from <0.104.0>
8ok
9
10%% まだプロセスが動いている
11> is_process_alive(Pid3).
12true
13
14> examples:rpc(Pid3, "Yo!").
15rpc[<0.102.0>]: Sent "Yo!" to <0.104.0>
16echo_loop[<0.104.0>]: Received "Yo!" from <0.102.0>
17rpc[<0.102.0>]: Received "Yo!" from <0.104.0>
18ok
19
20%% quitメッセージを送るとecho_loop/0関数から戻るのでプロセスが終了する
21> examples:rpc(Pid3, quit).
22rpc[<0.102.0>]: Sent quit to <0.104.0>
23echo_loop[<0.104.0>]: Received quit from <0.102.0>
24rpc[<0.102.0>]: Received "Bye." from <0.104.0>
25ok
26
27> is_process_alive(Pid3).
28false
ご質問のプログラムに戻ります。
図1の該当のソースコードのp_sleeping()
とp_wake_up()
のそれぞれをsleep
とwake
で登録したい。
p_sleeping/0
関数の中でstart(sleeping, S_Fun = fun() -> io:format("SLEEP!~n") end)
のようにするのではなく、generate_sleeping_process/0
関数でstart(sleep, fun p_sleeping/0)
のようにすれば登録できます。
erlang
1rpc(Pid) ->
2 %% 内容は変更なし
3
4p_sleeping() ->
5 receive
6 {Pid_rpc, start_sleep} ->
7 %% ここでstart/2を呼ぶのはやめる
8 S_Fun = fun() -> io:format("SLEEP!~n") end,
9 Pid_rpc ! {self(), S_Fun}
10 end.
11
12p_wake_up() ->
13 receive
14 {Pid_rpc, start_wake_up} ->
15 %% ここでstart/2を呼ぶのはやめる
16 W_Fun = fun() -> io:format("Wake Up!~n") end,
17 Pid_rpc ! {self(), W_Fun}
18 end.
19
20generate_sleeping_process() ->
21 %% start/2を呼ぶように変更する
22 start(sleep, fun p_sleeping/0).
23
24generate_wake_process() ->
25 %% start/2を呼ぶように変更する
26 start(wake, fun p_wake_up/0).
27
28start(Name, Fun) ->
29 register(Name, spawn(Fun)),
30 io:format("Finish_register~n").
実行結果
> c("exercises_v1").
{ok,exercises_v1}
> exercises_v1:generate_sleeping_process().
Finish_register
ok
> exercises_v1:generate_wake_process().
Finish_register
ok
> PidS = whereis(sleep).
<0.95.0>
> PidW = whereis(wake).
<0.97.0>
> exercises_v1:rpc(PidS).
SLEEP!
ok
> exercises_v1:rpc(PidW).
Wake Up!
ok
%% p_sleeping/0関数は1回receiveすると戻るので、2回目の呼び出しはできない
> exercises_v1a:rpc(PidS).
Time out!
ok
ただし、上の最後のrpc(PidS)
呼び出しがtime outになることからわかるとおり、p_sleeping/0
関数とp_wake_up/0
関数はメッセージを1回ずつしか受信できません。メッセージを何度も受信させたい場合はecho_loop/0
関数のように自分自身を呼び出す必要があります。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/03/04 02:07