2016-11-01 7 views
1

Erlangの並行処理のしくみを理解しようとしています。テストのために、私は以下のモジュールがあります。並行プロセスの実行順序

server.erl:

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


loop() -> 

    receive 

     {foo, Msg_foo} -> 
      io:format("~w~n", [Msg_foo]), 
      loop(); 

     {bar, Msg_bar} -> 
      io:format("~w~n", [Msg_bar]), 
      loop(); 

     stop -> 
      io:format("~s~n", ["End server process"]), 
      true 

    end. 

process_a.erl

-module(process_a). 
-export([go_a/0]). 

go_a() -> 

    receive 

     {foo, Pid1} -> 
      Pid1 ! {foo, 'Message foo from process A'}, 
      go_a(); 

     {bar, Pid2} -> 
      Pid2 ! {bar, 'Message bar from process A'}, 
      go_a() 

    end. 

process_b.erl

-module(process_b). 
-export([go_b/0]). 

go_b() -> 

    receive 

     {foo, Pid1} -> 
      Pid1 ! {foo, 'Message foo from process B'}, 
      go_b(); 

     {bar, Pid2} -> 
      Pid2 ! {bar, 'Message bar from process B'}, 
      go_b() 

    end. 

client.erl

-module(client). 
-export([start/0]). 
-import(server, [loop/0]). 
-import(process_a, [go_a/0]). 
-import(process_b, [go_b/0]). 


go() -> 

    Server_Pid = spawn(server, loop, []), 

    Pid_A = spawn(process_a, go_a, []), 
    Pid_B = spawn(process_b, go_b, []), 

    Pid_A ! {foo, Server_Pid}, 
    Pid_B ! {bar, Server_Pid}, 

    Pid_A ! {bar, Server_Pid}, 
    Pid_B ! {foo, Server_Pid}, 

    Pid_A ! {foo, Server_Pid}, 
    Pid_B ! {foo, Server_Pid}, 

    Pid_A ! {bar, Server_Pid}, 
    Pid_B ! {bar, Server_Pid}. 


start() -> 
    go(). 

クライアントはプロセスAとプロセスBにメッセージを送信します。プロセスAはプロセスBにメッセージを送信し、プロセスBはサーバーにメッセージを送信します。

A foo 
B bar 
A bar 
B foo 
A foo 
B foo 
A bar 
B bar 

が、プログラムの出力は次のとおりです:メッセージの順序がある

'Message foo from process A' 
'Message bar from process A' 
'Message foo from process A' 
'Message bar from process A' 
'Message bar from process B' 
'Message foo from process B' 
'Message foo from process B' 
'Message bar from process B' 

サーバが最初のプロセスAからのすべてのメッセージ、私の質問があり、プロセスBからのすべてのメッセージ、何を処理しますメッセージの処理順序を決定しますか?私はそれがメッセージが受け取られた順序だと思った。

答えて

4

すべてはプロセススケジューリングに依存します。クライアントコードがサーバーとprocs AとBを起動した後で、それらのプロセスは新しく作成されますが、まだ実行する時間は与えられていないかもしれません(もしそうであれば、それらは受信で直ちに中断されます)。クライアントコードは実行を継続し、AとBへの一連のメッセージを素早く送ります。これらは非同期操作であり、クライアントプロセスはgo()の呼び出しから戻る前に一時停止する必要はありません。

一時停止したプロセスがメッセージを取得するとすぐに実行のスケジュールを設定できますが、これが発生するまでには少し時間がかかることがあります。一方で、より多くのメッセージがメールボックスに到着し続けることがあるため、AまたはBが実際に実行を開始すると、メールボックスに既に存在するクライアントからの4つのメッセージすべてを受け取る可能性が高くなります。一般的に、スケジューリングはおそらくこのような単純なケースでは非常に予測可能ですが、AとBのどちらが最初に実行を開始するのかを確かめることはできません。

あなたの場合、AはBの前にスケジュールされ、実行を開始し、非常に短時間ですべてのメッセージを消費します。これは多くの作業を必要としないので、Aは時間切れで過ごすことさえありません。その後、メールボックスが空であるため中断されます。それから、Bはスケジュールされ、同じことをします。

多くのプロセスや作業があった場合、Erlang VMは異なるOSスレッド(マルチコアCPUを使用している場合は真に並列実行)でスケジューラ間でプロセスを分割する可能性があります。しかし、この例は非常に単純なので、これらのプロセスはおそらく単一のスケジューラ内で処理されるため、順序付けはさらに予測可能になります。 AとBの両方にキュー内に何千ものメッセージがある場合、または各メッセージが処理に多くの労力を要した場合、メッセージがインターリーブされて表示されます。

(ちなみに、spawn(Module、Fname、Args)を使用しているので、クライアントのimport宣言は何もしません。例えばspawn(fun() - > loop()end) )

+0

ありがとうございました! – Anonimista