concurrency - Erlang中的并发及其适当的工作流程

标签 concurrency erlang

该代码是由我们的老师提供给我们的,但可悲的是,没有得到解释。我们只是在课堂上尝试过,然后被开除。

如果有人可以向我彻底解释此代码,那将非常有帮助。提前致谢。

-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作为第一个参数来调用它。由于30不匹配,因此该进程以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/

相关文章:

java - 在 Java ArrayList(或类似的东西)上并发读取和设置

java - Lmax Disruptor,许多消费者 - 如何让消费者只接受特定类型的消息并独立进行?

erlang - 为什么这个带有类型签名的 erlang prog 可以编译?

file - Erlang,如何创建带有换行符的文本文件?

java.util.concurrent:从Runnable接口(interface)过渡到Executor接口(interface)

docker 中的 Python – RuntimeError : can't start new thread

java - 我不明白线程在这种情况下是如何工作的

xml - 使用Erlang xmerl_xpath提取xml文本时出现问题

process - 为什么这个 erlang 代码不起作用?

erlang - 在 Erlang 中,如何编写 BFS 树搜索来查找特定节点