什么';介绍';保存队列';在erlang接收语句中

什么';介绍';保存队列';在erlang接收语句中,erlang,Erlang,我是erlang新手,从Joe Armstrong的“编程erlang”开始学习教程 我对8.6中选择性接收中提到的“保存队列”感到困惑。如果消息完全不匹配,为什么不直接删除它?将其放回邮箱以便稍后处理的目的是什么?如果这是默认行为,那么这些垃圾消息(意味着它们无法匹配)可能会导致性能损失,因为它们会累积而不释放 我想知道我是否误解了这一部分。我试图检查流程邮箱的内容以获得更好的理解,但未能做到这一点 请帮忙,我会很感激任何能证明这一点的代码片段,谢谢。本书的这一段描述了一个接收集团所做工作的细

我是erlang新手,从Joe Armstrong的“编程erlang”开始学习教程

我对8.6中选择性接收中提到的“保存队列”感到困惑。如果消息完全不匹配,为什么不直接删除它?将其放回邮箱以便稍后处理的目的是什么?如果这是默认行为,那么这些垃圾消息(意味着它们无法匹配)可能会导致性能损失,因为它们会累积而不释放

我想知道我是否误解了这一部分。我试图检查流程邮箱的内容以获得更好的理解,但未能做到这一点


请帮忙,我会很感激任何能证明这一点的代码片段,谢谢。

本书的这一段描述了一个接收集团所做工作的细节。不要忘记,一个进程可以顺序处理多个接收组,因此一条与第一个接收组的任何条目都不匹配的消息将被放入保存队列(以提高效率),因为:

  • 它永远不会匹配当前接收组的任何条目
  • 只有当一条输入消息与一条输入匹配或超时结束时,才能保证当前接收区完成
当一个接收组完成时,保存队列将按原始接收顺序放回邮箱中,因为下一个接收组有机会有一个与“保存队列”消息之一匹配的条目

一种用法是管理优先级:

loop() ->
   ...
   receive
      {high_prio,Msg} -> process_priority_message(Msg)
   after 0
      ok  % no priority message
   end,

   receive
      {low_prio,Msg} -> process_normal_message(Msg);
      Other -> process_unexpected_message(Other)
   end,
   ...
   loop()
此代码允许处理消息{high_prio,Msg},即使它不在消息队列中的第一个位置

你是对的,有一种风险,即意外的消息会堆积在邮箱中,特别是在一个永无止境的循环中,这就是为什么你会经常看到像最后一行这样的东西

其他->处理意外消息(其他)


清空邮箱。

在构建并发系统时,这是一个巨大的帮助,因为它允许您只专注于您感兴趣的邮件,而忽略其他邮件。Erlang系统通常是不确定的,所以您很少知道将接收什么以及何时接收。如果没有自动保存队列,这意味着在每个接收点,您都必须能够处理可能到达该进程的每条消息。它很快就变成了状态和信息的组合爆炸

以简单服务器为例。在其顶层将有一个接收循环,用于接收服务器要处理的请求。然后它将处理第一个请求。在这个过程中,它很可能与其他进程通信,并接收消息。在处理请求时,新的请求消息可以到达服务器。如果Erlang没有保存消息,那么您必须在接收消息的服务器代码中的任何地方处理这些请求。现在您可以忽略这些新请求,并将它们留给应该处理它们的顶层循环

跟踪服务器中需要处理的所有消息很快就会变得不可行。例如,在
gen_服务器中
除了处理请求所需的所有消息外,还有客户端发送的实际请求(未指定消息格式)、“系统”消息(未指定消息格式)、对服务器代码有意义的任意数量的定义良好的消息

最终,您将实现自己的消息缓冲区并将其传递

如果没有消息保存队列,则几乎不可能编写在处理时发送/接收消息的通用模块,例如,将客户端功能写入
genu server
s。他们必须知道可能到达该过程并需要处理的每一条信息

是的,保存所有消息可能是个问题,但这通常是当您意识到它时可以解决的问题类型。例如,在服务器的顶部循环中,您可以合理地确定可以接收并丢弃未知消息。
gen_服务器
会在其顶层接收所有消息,其中一些消息会自行处理(系统消息),而其他消息则会传递给特定的服务器代码

正如@Pascal所示,它允许您轻松地处理优先级消息。

+1对Rbirding来说,“在构建并发系统时,这是一个巨大的帮助”,这正是它的意义所在。让我想起了一些示例代码——一个“吸烟者问题”解决方案——不久前我启动了。我认为分享它可能有助于说明这一点:

-module(smokers).
-export([smokers/0]).

smokers() ->
    Rounds = 1000000,
    Agent = self(),
    lists:foreach(fun(Material) -> spawn(fun() -> startSmoker(Agent, Material) end) end, materials()),
    done = agent(Rounds),
    io:format("Done ~p rounds~n", [Rounds]).

agent(0) ->
    done;
agent(Rounds) ->
    offer(twoRandomMaterials(), Rounds).

offer(AvailableMaterials, Rounds) ->
    receive
        {take, Smoker, AvailableMaterials} ->
            Smoker ! smoke,
            receive
                doneSmoking ->
                    agent(Rounds - 1)
            end
    end.

startSmoker(Agent, Material) ->
    smoker(Agent, lists:delete(Material, materials())).

smoker(Agent, Missing) ->
    Agent ! {take, self(), Missing},
    receive
        smoke ->
            Agent ! doneSmoking,
            smoker(Agent, Missing)
    end.

twoRandomMaterials() ->
    Materials = materials(),
    deleteAt(random:uniform(length(Materials)) - 1, Materials).

materials() ->
    [paper, tobacco, match].

deleteAt(_, []) -> [];
deleteAt(0, [_ | T]) -> T;
deleteAt(Idx, [H | T]) -> [H | deleteAt(Idx - 1, T)].

这里有趣的是,当我们尝试
接收{take,Smoker,AvailableMaterials}
时,队列中可能有三条消息。但是,现在只能处理其中一个。然后是作为握手的内部
接收doneSmoking
。因此,对于其中一个,选择适当的消息,允许代码在接收握手消息时执行一些工作,同时不丢失另一个
take
消息,这就解决了这里的所有并发性问题。如果在任何时候丢弃不匹配/不可处理的消息,
吸烟者将永远被卡住(除非他们会定期重复他们的请求)。

非常感谢,了解此设计非常有帮助。将
上的匹配更改为
匹配一个