Erlang进程消息接收



我正在使用Erlang进行并行计算训练。我有以下代码,用来计算圆周率:

start(To)->
    Self = self(),
    Cores = erlang:system_info(schedulers_online),
    {Step, Delta} = {1/To, round(To / Cores)},
    Pids = lists:map(fun (El) ->
        spawn(fun()->
            Self ! {sum, calculate(from(El, Delta), to(El, Delta), Step)}
        end)
    end, lists:seq(1, Cores)),
    lists:sum([receive {sum, S} -> S end || _ <- Pids ]) * Step.

现在,它对我来说很神奇

lists:sum([receive {sum, S} -> S end || _ <- Pids ])

请解释一下这种魔法是如何运作的?

lists:sum([receive {sum, S} -> S end || _ <- Pids ])

让我们一部分接一部分,首先是函数sum,它只是对列表中的所有元素求和,即:

1> lists:sum([1, 2, 3]).
6

lists:sum(SomeList) * Step.将在SomeList的元素和步骤中的值之间进行乘法运算。

有趣的部分是列表是如何构建的,它是由以下代码构建的:

[receive {sum, S} -> S end || _ <- Pids ]

你所拥有的是列表理解。Pids是一个Erlang列表,它包含您创建的每个进程的进程ID(PID),用于处理上的pi数

Pids = lists:map(fun (El) ->
    spawn(fun()->
        Self ! {sum, calculate(from(El, Delta), to(El, Delta), Step)}
    end)
end, lists:seq(1, Cores)).

假设你有4个核心,这个函数将创建4个进程,可能有一个像[<0.36.0>, <0.38.0>, <0.40.0>, <0.42.0>]这样的列表,最重要的想法是,如果你有四个核心,你将创建四个进程,如果你拥有八个核心,八个进程,等等

每个进程都将调用函数calculate,并将结果作为消息发送到Self,您可以在这里看到:

Self ! {sum, calculate(from(El, Delta), to(El, Delta), Step)}

因此,如果你有4个元素,你可以尝试这样做:

6> [X || X <- [1, 2, 3, 4]].
[1,2,3,4]

在那里,您将使用列表中的每个元素构建一个列表压缩。现在,假设您不关心用于构建新列表的列表元素,您可以执行以下操作:

7> [1 || _ <- [1, 2, 3, 4]].
[1,1,1,1]

您将使用列表压缩作为bucle,您并不真正关心用于构建新列表的列表元素,正是在这种情况下,您正在执行以下操作:

[1 || _ <- Pids ]

您并不真正关心Pids的内容,而是对列表Pids的元素数量感兴趣。

因此,如果列表Pids有4个元素,您将执行四次接收功能:

receive 
    {sum, S} -> 
        S 
end

该接收函数将侦听发送到进程的任何消息,其模式为{sum, S},与您使用{sum, calculate(from(El, Delta), to(El, Delta), Step)}发送的模式相同。函数的返回值为S,在这种情况下,它将是calculate(from(El, Delta), to(El, Delta), Step)的结果。

最后,您将得到一个包含4个元素的列表(同样,假设您有4个核心),其结果由您开始的四个过程计算得出。

最新更新