该代码是由我们的老师提供给我们的,但可悲的是,没有得到解释。我们只是在课堂上尝试过,然后被开除。
如果有人可以向我彻底解释此代码,那将非常有帮助。提前致谢。
-module(pingpong).
-compile(export_all).
start_pong() ->
register(pong, spawn(pingpong,pong,[])).
pong() ->
receive
finished ->
io:format("Pong finished ~n");
{ping, Ping_Pid} ->
io:format("i am the receiver ~n"),
Ping_Pid ! pong,
pong()
end.
start_ping(Pong_Node) ->
spawn(pingpong, ping, [3, Pong_Node]).
ping(0, Pong_Node) ->
{pong, Pong_Node} ! finished,
io:format("Pong finished ~n");
ping(N, Pong_Node) ->
{pong, Pong_Node} ! {ping, self()},
receive
pong ->
io:format("i am the sender ~n")
end,
ping(N-1,Pong_Node).
最佳答案
让我们看一下前两行。
-module(pingpong).
-compile(export_all).
第一个是模块声明,其参数是一个原子(或换句话说,是一个小写的单词,不带引号)。取自“学到一些Erlang”:
-module(Name).
这始终是文件的第一个属性(和语句),并且有充分的理由:这是当前模块的名称,其中Name是原子。这是您用来从其他模块调用函数的名称。调用以
M:F(A)
形式进行,其中M
是模块名称,F
函数,而A
参数。第二句话告诉编译器将所有声明的函数公开,即,您在该模块上编写的每个函数
F
都可以被外部人员称为pingpong:F
调用。这可能会简化您初学时的过程,但这通常是一个不好的做法。请参阅this question。
现在让我们看一下这些函数。
start_pong() ->
register(pong, spawn(pingpong,pong,[])).
这可能是您的代码开始的地方。您编译模块,然后在给定机器或节点的Erlang Shell中调用
pingpong:start_pong().
。此功能的全部作用是“使用spawn
将名称pong注册为要创建的进程的标识符”。因此,
spawn
创建一个Erlang进程。 spawn
也是内置函数(BIF),因此不需要在其模块名称前添加前缀。如in the documentation所示,其参数为spawn(Module, Exported_Function, List of Arguments)
。回顾
start_pong
,它所做的只是“创建一个进程,该进程将在此模块中运行不带任何参数的pong
函数,然后调用该进程pong”。pong() ->
receive
finished ->
io:format("Pong finished ~n");
{ping, Ping_Pid} ->
io:format("i am the receiver ~n"),
Ping_Pid ! pong,
pong()
end.
start_pong
中新创建的进程将运行此功能。 Erlang中的每个进程都有自己的邮箱。进程通过在这些邮箱中保留消息来彼此通信。消息可能几乎是任何东西。将它们视为您希望在流程之间发送的一些数据。新进程进入
receive
语句,该语句告诉它从其邮箱中提取一条消息,或者等到有消息出现。然后,当收到消息时,它使用模式匹配来找到适当的操作。如果您习惯使用命令式语言,请将其视为switch
,否则请忽略此语句。如果该进程包含单个原子
finished
的消息,它将在控制台中显示Pong finished
并退出。如果该进程的消息与原子
ping
和一个进程标识符(pid-每个进程都有一个)成对,则它将执行该函数的其余代码。大写的
Ping_Pid
告诉Erlang将消息具有的第二个值分配给名称为Ping_Pid
的变量。碰巧您希望获得pid。进入这种情况时,它的工作是打印
i am the receiver
,然后将带有原子pong
的消息发送到Ping_Pid
所标识的进程-这就是!
运算符的作用。最后,该函数调用自身,以便再次查看邮箱。您将在控制台(可能是在另一个节点/机器上)上写的下一个内容是对
start_ping
的调用。start_ping(Pong_Node) ->
spawn(pingpong, ping, [3, Pong_Node]).
如我们之前所见,所有这些操作都是创建一个运行
ping
函数的进程,该进程具有参数3
和它接收到的Pong_Node
,它是第一个进程正在运行的机器(节点)。ping(0, Pong_Node) ->
{pong, Pong_Node} ! finished,
io:format("Pong finished ~n");
ping(N, Pong_Node) ->
{pong, Pong_Node} ! {ping, self()},
receive
pong ->
io:format("i am the sender ~n")
end,
ping(N-1,Pong_Node).
该函数在两种情况下定义(请注意,第一个
ping
块以;
而不是.
结尾-这告诉Erlang还有更多要定义的函数)。您以
3
作为第一个参数来调用它。由于3
与0
不匹配,因此该进程以N
作为参数执行第二种情况。此过程将对
{ping, self()}
发送到{pong, Pong_Node}
给定的过程,该过程遵循语法{registered_name, node_name}
。 self()
用于检索当前进程自己的pid。此后,进程将等待
pong
响应,并在N
大于零时再次重复该响应。当
N
达到零时,将执行第一种情况,将finished
发送到{pong, Pong_Node}
,并结束执行。如果您认为此说明不完整,则还可以查看at the tutorial,它描述了此确切程序。
关于concurrency - Erlang中的并发及其适当的工作流程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19075965/