技术开发 频道

Erlang编程入门之并发编程—消息传递

  【IT168 技术文档】下面的例子中,我们创建了两个进程,其中一个重复向另一个发送消息。

  -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”然后自己结束掉自己。

0
相关文章