【问题标题】:Concurrent process execution order并发流程执行顺序
【发布时间】:2017-03-15 01:15:45
【问题描述】:

试图弄清楚 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 发送消息,进程 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的所有消息。我的问题是,什么决定了消息处理顺序?我以为是收到消息的顺序。

【问题讨论】:

    标签: concurrency process erlang


    【解决方案1】:

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

    一旦暂停的进程收到消息,它就准备好被安排执行,但在此之前可能需要一小部分时间。同时,更多的消息可能会不断到达他们的邮箱,因此当 A 或 B 真正开始运行时,他们可能已经将来自客户端的所有 4 条消息都放在了他们的邮箱中。通常,您也无法确定 A 和 B 中的哪一个将首先开始执行,即使在像这样的简单情况下调度可能是非常可预测的。

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

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

    (顺便说一句,您在客户端中的导入声明什么都不做,因为您使用的是 spawn(Module, Fname, Args)。如果您编写了例如 spawn(fun() -> loop() end) 他们将是需要。)

    【讨论】:

      猜你喜欢
      • 2019-03-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-12
      • 2013-02-26
      • 1970-01-01
      • 2020-01-05
      • 2020-06-05
      相关资源
      最近更新 更多