Elixir 为什么是随机顺序
我有下面的模块,模拟平行地图Elixir 为什么是随机顺序,elixir,Elixir,我有下面的模块,模拟平行地图 defmodule Parallel do def pmap(collection, fun) do me = self collection |> Enum.map(fn (elem) -> spawn_link fn -> send(me, { self, fun.(elem) }) end end) |> Enum.map(
defmodule Parallel do
def pmap(collection, fun) do
me = self
collection
|> Enum.map(fn (elem) ->
spawn_link fn -> send(me, { self, fun.(elem) }) end
end)
|> Enum.map(fn (pid) ->
receive do { ^pid, result } ->
result
end
end)
end
end
我编译、运行并获得预期结果:
iex(5)> Parallel.pmap 1..1000, &(&1 * &1)
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256 ...]
当我从receive do{pid,result}->
中删除pin print操作符时,我将不再按正确的顺序获得列表:
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 256, 225, 289, 361...]
为什么pin运算符会影响订单 当您映射元素集合并为集合中的每个元素生成一个新进程时,您将返回一个PID列表:这些PID与您映射的集合的顺序相同,即。,给定
elem
的pid在pid列表中的位置与原始集合中的elem
相同。这就是映射的工作原理,您可以对列表的每个元素应用一个操作,并获取这些操作的结果列表
现在,您可以映射PID列表。当您在^pid
上进行匹配时,代码将被阻止,直到您要映射的当前pid
的消息到达当前进程。但是,{^pid,result}
消息可能不是当前进程消息队列中的唯一消息或第一个消息:因为所有生成的进程现在都并行运行,所以它们不会按照生成的顺序发送结果。这意味着,当您在{^pid,result}
上接收并匹配时,消息队列可以在匹配{^pid,result}
的消息之前有其他消息({pid\u 1,result\u 1}
,{pid\u 2,result\u 2}
)。由于Erlang中接收进程的工作方式,如果这些消息与receive
中的模式不匹配,则将跳过这些消息,直到其中一个匹配为止(或者我们一直等待新的匹配消息)
当您在{pid,result}
上进行匹配时,您的意思是任何两个元素元组都可以:在这种情况下,pid
很可能不是您当前映射的pid
,原因与我上面提到的完全相同(派生的进程将以不可预测的顺序返回结果)
更直观的表示:假设在派生进程开始运行后,当前进程中有此消息队列(我们将调用派生进程pid1
,pid2
,等等):
假设您当前正在映射pid1
(即,在传递给Enum.map/2
的函数中,pid
是pid1
)
当您执行接收do{^pid,res}->…
(使用pid==pid1
)时,第一条消息将不匹配;因此,下一条消息将与此消息匹配{pid3,res3}
将被放回消息队列,并且接收
将使用{pid1,res1}
执行。消息队列如下所示:
# The one on top is the first in the message queue:
{pid3, res3}
{pid4, res4}
{pid5, res5}
{pid2, res2}
回到原始队列:现在,如果您在
{pid,res}
上匹配(没有^
pin操作符),任何两个元素元组都将匹配;具体来说,{pid3,res3}
将匹配,接收
块将与之一起执行(即使我们映射的pid
是pid1
)。这意味着在结果列表中,第三个元素的结果(为其生成了pid3
)将取代第一个元素的结果:随机顺序 当您映射元素集合并为集合中的每个元素生成一个新进程时,您将返回一个PID列表:这些PID与您映射的集合的顺序相同,即。,给定elem
的pid在pid列表中的位置与原始集合中的elem
相同。这就是映射的工作原理,您可以对列表的每个元素应用一个操作,并获取这些操作的结果列表
现在,您可以映射PID列表。当您在^pid
上进行匹配时,代码将被阻止,直到您要映射的当前pid
的消息到达当前进程。但是,{^pid,result}
消息可能不是当前进程消息队列中的唯一消息或第一个消息:因为所有生成的进程现在都并行运行,所以它们不会按照生成的顺序发送结果。这意味着,当您在{^pid,result}
上接收并匹配时,消息队列可以在匹配{^pid,result}
的消息之前有其他消息({pid\u 1,result\u 1}
,{pid\u 2,result\u 2}
)。由于Erlang中接收进程的工作方式,如果这些消息与receive
中的模式不匹配,则将跳过这些消息,直到其中一个匹配为止(或者我们一直等待新的匹配消息)
当您在{pid,result}
上进行匹配时,您的意思是任何两个元素元组都可以:在这种情况下,pid
很可能不是您当前映射的pid
,原因与我上面提到的完全相同(派生的进程将以不可预测的顺序返回结果)
更直观的表示:假设在派生进程开始运行后,当前进程中有此消息队列(我们将调用派生进程pid1
,pid2
,等等):
假设您当前正在映射pid1
(即,在传递给Enum.map/2
的函数中,pid
是pid1
)
当您执行接收do{^pid,res}->…
(使用pid==pid1
)时,第一条消息将不匹配;因此,下一条消息将与此消息匹配{pid3,res3}
将被放回消息队列,并且接收
将使用{pid1,res1}
执行。消息队列如下所示:
# The one on top is the first in the message queue:
{pid3, res3}
{pid4, res4}
{pid5, res5}
{pid2, res2}
回到原始队列:现在,如果您在{pid,res}
上匹配(没有^
pin操作符),任何两个元素元组都将匹配;具体来说,{p
{pid3, 9}
{pid2, 4}
{pid1, 1}