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的咒语......
答案 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有一个关于选择性接收的部分非常好。