代码之家  ›  专栏  ›  技术社区  ›  NeoWang

如何理解Erlang的receive语句?

  •  1
  • NeoWang  · 技术社区  · 11 年前

    我是二郎新手,所以请对我宽容一点。

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

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

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

    我想不会,因为我看到很多代码会重复执行receive语句:

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

    但如果messageA的处理程序包含另一个receive语句,则存在一个问题:

    loop() ->
       receive
           MessageA -> 
              loop2(),
           MessageB -> 
              handlerB(),
              loop()
       end
    
     loop2() ->
       receive
          MessageC ->
              handlerC()
              loop2()
          MessageD ->
              handlerD()
              loop2()
       end
    

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

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

    有更好的方法吗?

    2 回复  |  直到 11 年前
        1
  •  4
  •   Vatine    11 年前

    下面的代码表示“执行接收单个消息”,因此如果您希望接收多个消息,则需要循环。在Erlang中,这样做的典型方式是尾呼自己。

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

    在上一个示例中,看起来您有某种状态机,其中A变为另一个状态,而B保持相同状态。当您处于需要C和D消息的状态时,无法再接收a消息并不一定是问题,但这取决于问题域。

        2
  •  2
  •   Pascal    11 年前

    你明白了。

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

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

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

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