首页 > Erlang快速入门 > 4.2 Erlang快速入门之消息传递
2013
11-03

4.2 Erlang快速入门之消息传递

BACK TOP文章索引

  1. Erlang消息传递
  2. 共1条评论

Erlang消息传递

下面的例子中,我们创建了两个进程,其中一个重复向另一个发送消息。

-module(tut15).
-export([start/0, ping/2, pong/0]).
ping(0, Pong_PID) ->
    Pong_PID ! finished,
    io:format("ping finished~n", []);
ping(N, Pong_PID) ->
    Pong_PID ! {ping, self()},
    receive
        pong ->
            io:format("Ping received pong~n", [])
    end,
    ping(N - 1, Pong_PID).
pong() ->
    receive
        finished ->
            io:format("Pong finished~n", []);
        {ping, Ping_PID} ->
            io:format("Pong received ping~n", []),
            Ping_PID ! pong,
            pong()
    end.
start() ->
    Pong_PID = spawn(tut15, pong, []),
    spawn(tut15, ping, [3, Pong_PID]).  

编译运行:

  
1> c(tut15).
{ok,tut15}
2> tut15: start().
<0.36.0>
Pong received ping
Ping received pong
Pong received ping
Ping received pong
Pong received ping
Ping received pong
ping finished
Pong finished    

函数start首先创建了一个进程,我们叫它做“pong”:
Pong_PID = spawn(tut15, pong, [])
这个进程执行tut15:pong()。Pong_PID是这个进程“pong”的标识符。函数start现在要创建另一个进程“ping”了。
spawn(tut15, ping, [3, Pong_PID]),
这个进程执行:
tut15:ping(3, Pong_PID)
<0.36.0> 是函数start的返回值。
进程“pong”现在做:

receive
    finished ->
        io:format("Pong finished~n", []);
    {ping, Ping_PID} ->
        io:format("Pong received ping~n", []),
        Ping_PID ! pong,
        pong()
end.    

receive 关键词被用来让进程等待从其他进程发来的消息,格式如下:

receive
   pattern1 ->
       actions1;
   pattern2 ->
       actions2;
   ....
   patternN
       actionsN
end.    

注意:在end之前没有“;”。
在Erlang进程之间传递的消息都是简单的合法的Erlang“短语(Term)”。可以是列表、元组、整数、常量或者pid什么的。
每个进程都有自己的输入消息队列,用以接受消息。新的消息到达该进程时被放在队列的末尾。当一个进程执行一个receive,队列中的第一个消息被receive中的第一个模式(pattern)匹配测试,如果匹配,该消息从消息队列中删除,并且执行对应pattern下的操作。
如此,如果第一个pattern没有被匹配成功,第二个模式就将被测试,如果匹配成功,该消息就会从消息队列中删除,并且执行对应pattern下的操作。如果第二个pattern仍然不能被测试为真,则该过程以此类推,直到没有pattern可供测试为止。如果没有pattern可供测试了,第一个消息将被保存在在消息队列中,并且开始第二个消息的匹配测试工作,如果这时第二个消息匹配成功了,则删除消息队列中的第二个消息,但是第一个消息和其他消息的状态并不受到任何影响。如果第二个消息还是匹配失败,则该过程持续下去,依次进行第三个、第四个消息的匹配。如果我们到了队列的末尾,该进程被阻塞(停止执行),并且等待新消息的到来,然后重新开始匹配的过程。
当然,Erlang的实现是非常“聪明”的,并且能够最小化每个消息被接收方的receive测试的次数。
现在回到我们的ping pong例子。
“Pong” 等待着消息。如果常量finished被接收到,“pong”将输出“Pong finished”,然后继续“无所事事”。如果它接收一个消息是下面的格式:

{ping, Ping_PID}   

它输出“Pong received ping”,并且发送常量pong到进程“ping”:

Ping_PID ! pong 

注意:操作符“!”怎样被用来发送消息的,下面是“!”的语法:

Pid ! Message  

消息(可以是任何的Erlang的Term)被用来向Pid标识的的进程发送消息。
在pong发送消息到进程“ping”后,“pong”再次调用了pong函数,这就让它回到了等待接受消息的那种状态,从而等待其他消息的到来。现在我们来看进程“ping”。回忆它是怎么开始运行的:

tut15:ping(3, Pong_PID)   

看看ping/2,我们看到ping/2的第二个子句被执行,并且带有参数3(不是0)(第一个子句是ping(0,Pong_PID),第二个子句是ping(N,Pong_PID),这里的N会被赋值为3)。
第二个子句发送到消息到“pong”:
Pong_PID ! {ping, self()},
self() 返回当前执行self()进程的pid,在这里就是“ping”的pid。(回忆“pong”的代码,看看里面Pong_PID的情况)
“Ping”现在等待着从“pong”传回的信息:

receive
    pong ->
        io:format("Ping received pong~n", [])
end,  

并且当回复的消息到达时会输出“Ping received pong”,之后“ping”会再次调用ping函数。

ping(N - 1, Pong_PID)   

N-1 使得第一个参数递减,直到减到0为止。当减到0时,第一个子句ping/2会被执行:

ping(0, Pong_PID) ->
    Pong_PID !  finished,
    io:format("ping finished~n", []);    

常量finished被发送给“pong”(这将导致接受方终止)并且输出“ping finished”。“ping”然后自己结束掉自己。


4.2 Erlang快速入门之消息传递》有 1 条评论

  1. langer 说:

    这教程通俗易懂啊,感觉比 erlang 程序设计 讲得还好。。

留下一个回复

你的email不会被公开。