【问题标题】:Erlang Code understandingErlang代码理解
【发布时间】:2015-02-25 03:44:00
【问题描述】:

您好,我正在通过了解一些基本的 Erlang 服务器模块来进行一些 Erlang 培训。但是我被困在这个问题上。在这个模块中,我应该告诉 3 个进程(父进程和 2 个子进程)中的每一个可以打印的最低和最高可能值,因为根据特定的执行顺序,3 个进程可能会打印不同的值。

我执行了测试,得到了 2 个父进程,3 个子进程,但我不知道他们是如何得到这些值的。有人可以具体向我解释一下这个过程吗?赞赏。

这是模块:

-module(p4).
-export([start/0, init/0, read/1, incr/1, reset/1, test/0]).

start()->
    spawn(fun() -> init() end).
init() -> loop(0).

loop(N) ->
    receive
   {read, Pid} ->
	    Pid ! {value, self(), N},
	    loop(N);
	{incr, Pid} ->
            Pid ! {incr_reply, self()},
	    loop(N+1);
	{reset, Pid} ->
            Pid ! {reset_reply, self()},    
	    loop(0)
   end.

read(Serv) ->
    Serv ! {read, self()},
    receive {value, Serv, N} -> N end.

incr(Serv)->
    Serv ! {incr, self()},
    receive{incr_reply, Serv} -> ok end.

reset(Serv) ->
    Serv ! {reset, self()},
    receive {reset_reply, Serv} -> ok end.
	    
test() -> 	  
    Server = start(),
    spawn(fun() -> incr(Server),
		   io:format("Child 1 read ~p~n", [read(Server)]) end),
    incr(Server),
    spawn(fun() -> incr(Server),
	           io:format("child 2 read ~p~n", [read(Server)]) end),
    io:format("Parent read ~p~n", [read(Server)]).
		  

【问题讨论】:

    标签: erlang concurrent-programming


    【解决方案1】:

    理解 Lukasz 答案的一个精确度是所有服务器接口(读取、增量、重置)都是同步的:它们等待来自服务器的回答。这意味着在服务器完成请求之前,使用这些接口的进程不能做任何事情。证明 child2 不能读取小于 2 是非常重要的。

    2个序列图可视化流程:

    【讨论】:

    • 所以如果incr(Server) 只是发送消息而不等待确认,那么孩子 2 也可以读取 1 吗?我稍微修改了代码(删除了子 1 以增加机会)并使用数百万个进程运行了许多大规模测试,而子 2 从未读过 1。我找不到任何关于消息传递实现的简单描述,但根据我对源代码的理解(真的很差)消息传递和消息队列,孩子 2 无论如何都无法读取 1。但这只是一种实现而不是范式,对吧?
    • 我猜如果运行服务器的核心忙于其他事情,不同的消息可能正在等待由这个核心一起处理(这是一个猜测,我不知道它是如何在不同版本的光束机中实现)。那么有可能先取child2的incr和read消息。至少不能保证它永远不会发生。
    【解决方案2】:

    尝试打印任何服务器正在接收的消息(或使用调试器跟踪)以便更好地理解,你应该得到类似的东西:

    Server state was: 0 and received: {incr,<0.59.0>}
    Server state was: 1 and received: {incr,<0.109.0>}
    Server state was: 2 and received: {read,<0.59.0>}
    Server state was: 2 and received: {incr,<0.110.0>}
    Parent read 2
    Server state was: 3 and received: {read,<0.109.0>}
    Server state was: 3 and received: {read,<0.110.0>}
    Child 1 read 3
    child 2 read 3
    

    &lt;0.59.0&gt; 是父进程,&lt;0.109.0&gt; 是子进程 1,&lt;0.110.0&gt; 是子进程 2。

    这意味着父 read 消息在子进程的 read 之前传递,但在第一个子进程 incr 之后传递。但它不一定是这样的。这取决于进程调度。您唯一的保证是从进程 A 发送到进程 B 的消息将以相同的顺序传递。 由于incr(Server)read(Server) 的同步性质,这并不重要。因为这些进程中的每一个都在read(Server) 之前运行incr(Server),所以它必须至少得到1,但请注意,子进程2 是在父进程执行incr(Server) 之后产生的,这是同步操作,因此它在运行自己的@987654333 时必须至少为1 @,因此读取时必须至少为 2。每个最大值为 3(incr(Server) 的总数,意味着每个 read(Server) 中的任何一个都可能延迟)。

    可能的打印值汇总:父级:1,2,3;孩子 1:1、2、3;孩子 2:2,3

    简化的执行顺序:

    您的情况(父母获得 2,两个孩子获得 3):

    parent: spawn server
    parent: spawn child 1
    parent: incr(Server)
    child 1: incr(Server)
    parent: spawn child 2
    parent: io:format("parent read ~p~n",[read(Server)]) % prints 2
    child 2: incr(Server)
    child 1: parent: io:format("child 1 read ~p~n",[read(Server)]) % prints 3
    child 2: parent: io:format("child 2 read ~p~n",[read(Server)]) % prints 3
    

    孩子 1 的最小案例:

    parent: spawn server
    parent: spawn child 1
    child 1: incr(Server)
    child 1: io:format("child 1 read ~p~n",[read(Server)]) % prints 1
    ...
    

    父母的最大案例:

    parent: spawn server
    parent: spawn child 1
    parent: incr(Server)
    parent: spawned child 2
    child 1: incr(Server)
    child 2: incr(Server)
    Parent: io:format("child 1 read ~p~n",[read(Server)]) % prints 3
    ...
    

    我创建了大规模测试,它生成了 100000 个同时运行 test/0 的进程,并创建了外部 stat_server,它获取并计算每个进程 read(Server) resault,这里是:

    [{{child1,1},2}, % child 1 reads 1 only twice. Sometimes it's 1 sometimes it's 0, it varies
     {{child1,2},53629},
     {{child1,3},46369},
     {{child2,2},107},
     {{child2,3},99893},
     {{parent,1},855},
     {{parent,2},99112},
     {{parent,3},33}]
    

    【讨论】:

    • 你能告诉我哪些行指定消息 B 在来自同一进程的消息 A 之后发送将在 A 之后传递到目标进程吗?还有为什么这些进程中的每一个都在read 之前发送incr
    • @user1579701 每个进程在读取(服务器)之前运行 incr(服务器)。查看 read/1 的定义。 Spawn 函数只是创建新进程。它不会等到它完成或开始传递函数执行。
    • @user1579701 刚刚注意到我在孩子 2 的读取最小值的情况下的错误。编辑了我的答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-09
    相关资源
    最近更新 更多