Concurrency 并发进程执行顺序

Concurrency 并发进程执行顺序,concurrency,process,erlang,Concurrency,Process,Erlang,试图弄清楚Erlang并发是如何工作的。对于测试,我有以下模块: server.erl: -module(server). -export([loop/0]). loop() -> receive {foo, Msg_foo} -> io:format("~w~n", [Msg_foo]), loop(); {bar, Msg_bar} -> io:form

试图弄清楚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.
过程

-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.
过程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依次向服务器发送消息。信息的顺序为:

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的所有消息。我的问题是,什么决定了消息处理顺序?我认为这是接收消息的顺序。

这完全取决于进程调度。在客户机代码启动服务器和进程A和进程B之后,这些进程是新创建的,但可能还没有给它们任何执行时间(如果有,它们将立即挂起)。客户端代码持续执行,并快速向a和B发送一组消息。这些是异步操作,客户端进程在从调用go()返回之前根本不需要挂起

一旦挂起的进程收到消息,它就可以被安排执行,但这可能需要一小部分时间。同时,更多的消息可能会不断到达他们的邮箱,因此当A或B实际开始运行时,他们的邮箱中可能已经有来自客户端的所有四条消息。一般来说,您也无法确定A和B中的哪一个将首先开始执行,即使在这种简单的情况下,调度可能是非常可预测的

所以在你的例子中,A在B之前被调度,它开始执行,并且在很短的时间内消耗了它所有的消息。这不需要太多的工作,所以A甚至不会花费整个时间片。然后由于邮箱为空而挂起。然后B被安排做同样的事情

如果有许多进程和/或大量工作,Erlang VM可能会在不同OS线程上的调度程序之间拆分这些进程(如果您有多核CPU,则以真正并行的方式运行)。但由于示例非常简单,这些进程可能在单个调度器中处理,因此排序变得更加可预测。如果A和B的队列中都有数千条消息,或者每条消息都需要大量的计算来处理,那么您会看到消息被交错


(顺便说一句,客户端中的导入声明不起任何作用,因为您使用的是spawn(Module、Fname、Args)。如果您编写了例如spawn(fun()->loop()end),则需要它们。)

这一切都取决于进程调度。在客户机代码启动服务器和进程A和进程B之后,这些进程是新创建的,但可能还没有给它们任何执行时间(如果有,它们将立即挂起)。客户端代码持续执行,并快速向a和B发送一组消息。这些是异步操作,客户端进程在从调用go()返回之前根本不需要挂起

一旦挂起的进程收到消息,它就可以被安排执行,但这可能需要一小部分时间。同时,更多的消息可能会不断到达他们的邮箱,因此当A或B实际开始运行时,他们的邮箱中可能已经有来自客户端的所有四条消息。一般来说,您也无法确定A和B中的哪一个将首先开始执行,即使在这种简单的情况下,调度可能是非常可预测的

所以在你的例子中,A在B之前被调度,它开始执行,并且在很短的时间内消耗了它所有的消息。这不需要太多的工作,所以A甚至不会花费整个时间片。然后由于邮箱为空而挂起。然后B被安排做同样的事情

如果有许多进程和/或大量工作,Erlang VM可能会在不同OS线程上的调度程序之间拆分这些进程(如果您有多核CPU,则以真正并行的方式运行)。但由于示例非常简单,这些进程可能在单个调度器中处理,因此排序变得更加可预测。如果A和B的队列中都有数千条消息,或者每条消息都需要大量的计算来处理,那么您会看到消息被交错


(顺便说一句,客户端中的导入声明不起任何作用,因为您使用的是spawn(Module、Fname、Args)。如果您编写了例如spawn(fun()->loop()end),则需要它们。)

谢谢您的回答!谢谢你的回答!