【问题标题】:Understanding selective receives in Erlang理解 Erlang 中的选择性接收
【发布时间】:2019-12-25 08:18:42
【问题描述】:

我正在阅读LYAE 关于超时的章节,但我无法理解在以下情况下会发生什么:

important() ->
   receive
     {Priority, Message} when Priority > 10 ->
         [Message | important()]
   after 0 ->
     normal()
   end.

normal() ->
   receive
     {_, Message} ->
         [Message | normal()]
   after 0 ->
         []
    end.

1> c(multiproc).
{ok,multiproc}
2> self() ! {15, high}, self() ! {7, low}, self() ! {1, low}, self() ! {17, high}.      
{17,high}
3> multiproc:important().
[high,high,low,low]

我不明白以下内容:

  1. 根据我的理解,说after 0 就像使用,,如果消息在receive 子句中匹配,它会发生即使
  2. 读完第一条消息后,我们得到了[15, important()],在第二次迭代中将调用normal,所以我们只有一只手[15, important() --3-rd call][7,normal() -fourth call]。 那么最终我们如何以某种方式最终得到两个连接在一起的列表。
  3. 阅读前 2 条消息后:

important[15]
normal(第一次通话)与[7]

现在normal()(第一次呼叫)已经在等待新消息,而important 现在将第二次呼叫normal(),所以现在在第二次呼叫中我们不会有[1] 吗?

我不明白[7][1] 是如何合并的,因为它们来自对normal() 的单独调用。

我理解important(),因为结果被放在列表的末尾[Message,important()]。但normal() 的情况并非如此,因为它被important 调用并且每次它都应该创建一个新列表.

PS我添加了一张图片,以进一步解释我的困境。我想我现在明白了,最后两个分支会将他们的结果返回到[7,15],但我仍然不明白什么顺序。

【问题讨论】:

    标签: erlang timeout


    【解决方案1】:

    在 0 之后说就像 using ,据我了解,它会发生 即使消息在接收子句中匹配?

    错了。如果消息匹配,则跳过after

    阅读第一条消息后,我们得到 [15, important()],

    错了。 [15 | important()][15, important()] 不同。 15 是Priority——不是Message,但它是Message,例如high,被插入到列表中。

    不知何故,我们最终得到了两个连接在一起的列表。

    错了。邮箱中有以下消息:

    {15, high} 
    {7, low}
    {1, low}
    {17, high}
    

    第一个重要的()调用:

    {15, high} matches the important() receive
    return value: [high | important()]
    

    现在erlang需要调用important() again并将返回值代入[high | important()]

    以下邮件在邮箱中:

    {7, low}
    {1, low}
    {17, high}
    

    第二个重要()调用:

    {7, low} doesn't match the important() receive
    {1, low} doesn't match the important() receive
    {17, high} matches the important() receive
    
    return value: [high | important()]
    

    代入第一个重要返回值的结果:

              [high | important()] -- 2nd important() return value
                |
                V           
    [high | important()] --  1st important() return value
    

    给你:

    [high | [high | important()] ]
    

    现在,erlang 必须第三次调用 important() 并将第三次调用的返回值替换为该结果。

    以下邮件在邮箱中:

    {7, low}
    {1, low}
    

    第三次重要()调用:

    {7, low} doesn't match the important() receive
    {1, low} doesn't match the important() receive
    
    So, the after executes immediately--because the timeout is 0.
    return value: normal().
    

    将结果[high | [high | important()] ] 中的重要() 替换为normal() 可以得到:

    [high | [high | normal()] ]
    

    现在 erlang 必须评估 normal() 并将其返回值替换为该结果。

    第一次正常()调用:

     {7, low} matches the normal() receive
     return value:  [low | normal()]
    

    进行替换:

                 [low | normal()]
                       |
                       V
    [high | [high | normal()] ]
    

    给予:

    [high | [high | [low | normal()] ] ]
    

    以下邮件在邮箱中:

    {1, low}
    

    第二次普通()调用:

    {1, low} matches the normal() receive
    return value: [low | normal()]
    

    进行替换:

                            [low | normal()]
                              |
                              V
    [high | [high | [low | normal()] ] ]
    

    产生:

    [high | [high | [low | [low | normal()] ] ] ]
    

    以下邮件在邮箱中:

     <none>
    

    第三次正常通话:

    The after executes immediately because the timeout is 0.
    return value:  []
    

    代入:

                                    []
                                    |
                                    V
     [high | [high | [low | [low | normal() ] ] ] ]
    

    给予:

     [high | [high | [low | [low | [] ] ] ] ].
    

    这是什么乱七八糟的东西??!

    7> [high | [high | [low | [low | [] ] ] ] ].
    [high,high,low,low]
    

    在 erlang 中,列表是使用 cons 运算符 | 和嵌套子列表递归定义的。以下是一些其他示例:

    8> [1 | [2 | [] ]].
    [1,2]
    

    |(cons 运算符)右侧的东西必须是一个列表。 Erlang 语法也允许你这样写:

    10> [1, 2, 3 | [4, 5]].
    [1,2,3,4,5]
    

    同样,| 运算符右侧的东西必须是一个列表,但您可以在左侧写逗号分隔的值。

    【讨论】:

    • 对不起,我对 Haskell cons 运算符感到困惑。但是,当您使用 after TIMEOUT 时,这意味着它已经到达邮箱的末尾并等待 TIMEOUT 匹配模式的消息正确显示?
    • @BercoviciAdrian,它已经到了邮箱的末尾。 -- 嗯……我不知道。可能是计时器在检查任何消息之前启动。我不知道 erlang 是否保证 after 子句将在 timeout 毫秒内开始执行,或者等待实际上是否可能是 timeout 值的许多倍。
    【解决方案2】:

    receive 中,我们将在匹配子句之一或after 子句中结束。也就是说,after 子句只有在超时时间内没有匹配的消息时才会被评估。这与aftertry/catch 中的行为方式不同,catch 将无条件评估。


    这两个函数遵循一个常见的 Erlang 模式:通过递归调用构建列表 - 选择性 receive 在这里并不重要。注意这个表达式:

    [Message | important()]
    

    相当于:

    [Message] ++ important()
    

    也就是说,我们正在创建一个列表,其第一个元素是我们收到的消息,其尾部包含递归调用返回的任何内容。结果是我们得到一个接一个收到的消息列表。在某些时候我们切换到调用 normal() 而不是 important() 的事实并没有改变这一点 - 我们仍然一次构建一个元素的列表。

    一步一步:

    1. 我们从外壳调用multiproc:important()
    2. important 收到 {15, high},我们现在正在评估 [high] ++ important()
    3. important 的递归调用会忽略{7, low},因为它不匹配,但会接收到{17, high}。我们现在正在评估[high] ++ [high] ++ important()
    4. important 的第二次递归调用没有看到任何匹配的消息并进入after 子句。我们现在正在评估[high] ++ [high] ++ normal()
    5. normal 的调用收到{7, low},我们现在正在评估[high] ++ [high] ++ [low] ++ normal()
    6. normal 的递归调用接收{1, low},我们现在正在评估[high] ++ [high] ++ [low] ++ [low] ++ normal()
    7. normal 的第二次递归调用没有看到任何匹配的消息并返回[] 而不进行递归调用。我们现在有[high] ++ [high] ++ [low] ++ [low] ++ [],它等于[high, high, low, low]

    【讨论】:

    • 我添加了一张图片。
    • 对,似乎令人困惑的是,只要消息队列中有更高优先级的消息,我们就会忽略{7, low}。也就是说,由于我们在进行选择性接收,因此我们可以乱序接收消息,并将不匹配的消息留在队列中以备后用。
    • 所以你说 after 子句在队列中所有可以匹配的消息都被扫描之前不会被调用?它不能解决第一次传递的所有消息吗?假设它到达消息 nr 2,即7,它只是忽略它并且它不会将它发送到normal,直到它到达队列的末尾?我在想每条消息要么放在结果中,要么 after 子句被称为 ON THE FIRST PASSING
    • 没错。队列包含{7,low}, {1,low}, {17,high},但只有最后一条消息与{Priority, Message} when Priority &gt; 10 匹配,因此它会选择那个。只有当没有匹配的消息时才会进入after
    • 哦,我现在明白了....非常感谢您向我解释先生。因此,由于对normal 的调用和对important 的调用共享相同的mailboxafter 子句处理其余消息。
    猜你喜欢
    • 2012-06-13
    • 2017-11-28
    • 2020-09-11
    • 1970-01-01
    • 2012-03-15
    • 1970-01-01
    • 2010-10-31
    • 2015-04-20
    • 2016-03-19
    相关资源
    最近更新 更多