您可以使用列表(或其他集合)来指定在Erlang中接收哪些消息吗?

时间:2015-02-28 23:47:41

标签: erlang

E.g。假设我有一个类似于此的列表:

Handlers = [{foo, FooHandler}, {bar, BarHandler} | Etc()]

我能想到的最好的是:

receive
  Message ->
    Handler = find_matching_handler(Message, Handlers),
    Handler(Message)
end

问题在于,如果Message与处理程序中的任何内容都不匹配,则为时已晚:我已将其从邮箱中删除。

我想如果有一种方法可以将邮件放回邮箱(进入保存队列)而无需重新排序,那么就可以解决这个问题。只需重新发送到self()即可重新排序。它也不会重新启动接收,即使它确实如此,您可能会陷入旋转循环,直到感兴趣的消息到达。有没有办法将邮件放入邮箱的保存队列?

我想到的另一个近乎解决方案是使用匹配防护,但是IIUC,你只能在防护中使用BIF,这似乎排除了使用find_matching_handler(除非有BIF)。

另一个近乎解决方案:地图匹配:

receive
  M when Handlers#{M := Handler} -> Handler(M)  % booyah?
end

唉,我还没有找到满足Erlang的咒语......

2 个答案:

答案 0 :(得分:0)

匹配邮件:

loop() ->
  receive
    {foo, Data} ->
      handle_foo(Data),
      loop();
    {bar, Data} ->
      handle_bar(Data),
      loop()
  end.

这是区分消息表单的基本方法。

你也可以不那么直接并且在函数头中匹配你将所有消息传递给:

loop() ->
  receive
    Message ->
      handle_message(Message),
      loop()
  end.

handle_message({foo, Data}) ->
  foo(Data),
  ok;
handle_message({bar, Data}) ->
  bar(Data),
  ok.

第一种和第二种形式的组合是gen_server类型回调模块在OTP中构造的方式。消息处理程序接收一组稍微复杂的参数并存在于它们自己的模块(您编写的部分)中,而实际的receive出现在通用gen_server模块中。

答案 1 :(得分:0)

您可以使用选择性接收模式定期扫描邮箱以获取处理程序消息。像这样:

check_msg_handlers(Handlers) ->
  [check_handler(X) || X <- Handlers],
  timer:sleep(500),
  check_msg_handlers(Handlers).

check_handler(Handler) ->
  receive 
    {_Handler={M,F}, Msg} ->
      M:F(Msg)
  after 
    0 ->
      no_msg
  end.

注意receive X -> Y after -> N no_msg end,这是选择性接收。当使用N=0的超时时,它实际上变成对邮箱的扫描,以查看是否存在X消息,即它变为非阻塞接收。根据您的情况需要,扫描后会保留消息的顺序。

LYSE章节More On Multiprocessing有一个关于选择性接收的部分非常好。