如何理解Erlang的接收声明?

时间:2015-09-02 08:43:20

标签: erlang receiver message-handlers

我是Erlang的新手,所以请放轻松我。

我对如何在erlang中执行receive语句感到困惑,例如:

loop() ->
   receive
       MessageA -> handlerA();
       MessageB -> handlerB()
   end

如果接收到MessageA,并且执行了handlerA,一段时间后,在进程的收件箱中收到MessageB,handlerB会被执行吗?

我猜不是,因为我看到很多代码都会再次执行receive语句:

loop() ->
   receive
       MessageA -> 
          handlerA(),
          loop();
       MessageB -> 
          handlerB(),
          loop()
   end

但是这里有一个问题,如果messageA的处理程序包含另一个这样的接收语句:

loop() ->
   receive
       MessageA -> 
          loop2(),
       MessageB -> 
          handlerB(),
          loop()
   end

 loop2() ->
   receive
      MessageC ->
          handlerC()
          loop2()
      MessageD ->
          handlerD()
          loop2()
   end

在这种情况下,是否意味着如果我输入MessageA的处理程序,我永远无法处理MessageB?

我该如何解决这个问题?通过将MessageB的处理程序放入loop2?这看起来不太优雅,尤其是当有多个级别的receive语句时。

有更好的方法吗?

2 个答案:

答案 0 :(得分:3)

以下代码表示"执行接收单个消息",因此如果您想要接收多个消息,则需要循环。在Erlang中这样做的典型方法是给自己打电话。

loop() ->
   receive
       MessageA -> handlerA();
       MessageB -> handlerB()
   end

在上一个示例中,看起来您有某种状态机,其中A更改为另一个状态,而B保持相同状态。当您处于预期C和D消息的状态时,您不能再收到A消息,这不一定是问题,但这取决于问题域。

答案 1 :(得分:2)

你明白了。

关于loop和loop2的示例,这样的实现意味着您希望在收到messageA时选择一些新行为,如果稍后出现,您应该丢弃messageB。 (请注意,如果您使用带有大写字母的MessageA,它将成为变量名称,它将匹配任何消息!)。在这种情况下它是有道理的,你应该添加一个垃圾消息子句来从队列messageB和其他意外的消息中删除:

loop2() ->
   receive
      messageC ->
          handlerC(),
          loop2();
      messageD ->
          handlerD(),
          loop2();
      _ ->
          loop2()
   end.

另一种可能性是你实现了一种状态机,然后你应该使用OTP行为gen_fsm。

如果不是这种情况,意味着您仍然希望稍后收到消息B,我强烈建议您保留一个循环并在单个接收语句中处理所有可能的消息。