我正在使用Erlang进行并行计算培训。我有以下代码,计算pi编号:
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 ])
请解释这个魔法是如何运作的?
答案 0 :(得分:2)
lists:sum([receive {sum, S} -> S end || _ <- Pids ])
让我们逐个部分,首先是函数sum,它只是对列表中的所有元素求和,即:
1> lists:sum([1, 2, 3]).
6
lists:sum(SomeList) * Step.
只会在 SomeList 的元素总和与 Step 中的值之间进行乘法运算。
有趣的部分是如何构建列表,它是由代码构建的:
[receive {sum, S} -> S end || _ <- Pids ]
你所拥有的是List Comprehension。 Pids
是一个Erlang列表,其中包含您为处理pi编号而创建的每个进程的进程ID(PID):
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>]
这样的列表,主要的重要思想是,如果你有4个核心,你将创建4个进程,如果你有8个核心,8个进程,等等。
每个进程都会调用函数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]
并且您将使用列表压缩作为for 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个核心),其结果由您开始的四个进程计算。