1

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

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

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

如果收到 MessageA 并执行了 handlerA,一段时间后,在进程的收件箱中收到 MessageB,是否执行 handlerB?

我猜不是,因为我看到很多代码会递归地再次执行接收语句:

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的hander,就永远不能处理MessageB了?

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

有一个更好的方法吗?

4

2 回答 2

4

下面的代码意思是“执行接收单条消息”,所以如果要接收多条消息,就需要循环。在 Erlang 中执行此操作的典型方法是尾调用自己。

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

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

于 2015-09-02T09:08:28.830 回答
2

你说对了。

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

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

另一种可能性是您实现了一种状态机,那么您应该使用 OTP 行为 gen_fsm。

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

于 2015-09-02T09:26:58.073 回答