首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Erlang中的并发性及其相应的工作流

问Erlang中的并发性及其相应的工作流
EN

Stack Overflow用户
提问于 2013-09-29 08:28:58
回答 1查看 199关注 0票数 4

这个代码是我们老师给我们的,但遗憾的是,没有得到解释。我们刚在课堂上试过这个,后来被开除了。

如果有人能彻底地向我解释这段代码,那将是非常有帮助的。提前谢谢。

代码语言:javascript
复制
-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).
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2013-09-29 09:52:22

让我们看看前两行。

代码语言:javascript
复制
-module(pingpong).
-compile(export_all).

第一个是模块声明,它的参数是一个原子(换句话说,是一个小写单词,没有引号)。从你身上学到一些二郎

-module(Name). 这始终是文件的第一个属性(和语句),并且有充分的理由:它是当前模块的名称,其中name是一个原子。这是您将用于从其他模块调用函数的名称。调用使用M:F(A)表单进行,其中M是模块名,F是函数,A是参数。

第二句命令编译器将所有声明的函数公之于众,也就是说,您在该模块上编写的每个函数F都可以被外部调用为pingpong:F。

当你第一次学习的时候,这可能会简化你的学习过程,但这通常是一种糟糕的做法。请参阅这个问题。

现在让我们来看看这些函数。

代码语言:javascript
复制
start_pong() ->
    register(pong, spawn(pingpong,pong,[])).

这可能是您的代码开始的地方。编译模块,然后在给定机器或节点的Erlang中调用pingpong:start_pong().。这个函数所做的就是“注册名称pong作为我将要创建的进程的标识符,使用spawn__”。

因此,spawn创建了一个Erlang进程。spawn也是一个内置函数(BIF),因此不需要在它的模块名之前加上它。它的论点是spawn(Module, Exported_Function, List of Arguments),如在文件中所见。

回顾一下start_pong,它所做的就是“创建一个进程,在这个模块中运行不带参数的pong函数,并调用process pong__”。

代码语言:javascript
复制
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的调用。

代码语言:javascript
复制
start_ping(Pong_Node) ->
    spawn(pingpong, ping, [3, Pong_Node]).

正如我们前面看到的,所有这些都是创建一个将运行ping函数的进程,其中包含参数3及其接收的Pong_Node,这是第一个进程正在运行的机器(节点)。

代码语言:javascript
复制
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},并结束执行。

如果你觉得这个解释是不完整的,你也可以看看在教程中,它描述了这个精确的程序。

票数 7
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/19075965

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档