Erlang中的惯用进程同步

时间:2016-06-02 14:34:40

标签: erlang

我正在研究如何编码" map reduce"直接在erlang中输入方案。作为一个玩具示例,想象一下我想确定哪个文件最大。这些文件可能在互联网上的任何地方,因此获取每个文件可能需要一些时间;所以我想并行收集它们。一旦我拥有它们,我就可以比较它们的尺寸。

我假设的方法如下:

  • A' main'协调工作并确定哪个是最大的过程;
  • A'工人'每个文件的进程,它获取文件并将大小返回给主进程。

这是一个笨重但功能正常的示例(仅使用本地文件,但它显示了意图):

-module(cmp).
-export([cmp/2]).

cmp(Fname1, Fname2) ->
    Pid1 = fsize(Fname1),
    Pid2 = fsize(Fname2),
    {Size1, Size2} = collect(Pid1, Pid2),

    if 
        Size1 > Size2 ->
            io:format("The first file is bigger~n");
        Size2 > Size1 ->
            io:format("The second file is bigger~n");
        true ->
            io:format("The files are the same size~n")
    end.

 fsize(Fname) ->
    Pid = spawn(?MODULE, fsize, [self(), Fname]),
    Pid.

 fsize(Sender, Fname) ->
    Size = filelib:file_size(Fname),
    Sender ! {self(), Fname, Size}.

 collect(Pid1, Pid2) ->
    receive
        {Pida, Fnamea, Sizea} ->
            io:format("Pid: ~p, Fname: ~p, Size: ~p~n", [Pida, Fnamea, Sizea])
    end,
    receive
        {Pidb, Fnameb, Sizeb} ->
            io:format("Pid: ~p, Fname: ~p, Size: ~p~n", [Pidb, Fnameb, Sizeb])
    end,
    if
        Pida =:= Pid1 -> {Sizea, Sizeb};
        Pida =:= Pid2 -> {Sizeb, Sizea}
    end.

具体问题

  1. 这个方法是惯用的吗?也就是说,每次长时间跑步都要甩掉任务进入一个单独的过程,然后将结果收回到主人的
  2. 是否有用于处理同步机制的库?具体来说,上例中的collect函数是什么?
  3. 感谢。

    - 注意:我知道collect函数特别笨重;它可以通过例如推广来概括将pid存储在列表中,并循环直到所有已完成。

2 个答案:

答案 0 :(得分:2)

在我看来,最好从一个例子中学习,所以我看看他们是如何在otp/rpc中做到这一点的,并在此基础上我实现了parallel eval call的更短/更简单的版本。

call(M, F, ArgL, Timeout) ->
    ReplyTo = self(),
    Keys = [spawn(fun() -> ReplyTo ! {self(), promise_reply, M:F(A)} end) || A <- ArgL],

    Yield = fun(Key) ->
                    receive
                        {Key, promise_reply, {error, _R} = E}           -> E;
                        {Key, promise_reply, {'EXIT', {error, _R} = E}} -> E;
                        {Key, promise_reply, {'EXIT', R}}               -> {error, R};
                        {Key, promise_reply, R}                         -> R
                    after Timeout                                       -> {error, timeout}
                    end
            end,
    [Yield(Key) || Key <- Keys].

答案 1 :(得分:1)

我不是MapReduce专家,但我确实有使用this 3rd party mapreduce module的经验。所以我会根据我目前的知识来回答你的问题。

  1. 首先,您的输入应按键和值对排列,以便正确使用mapreduce模型。通常,您的进程应首先启动工作进程(或节点)。每个工作人员都会收到地图功能和一对密钥,并将其命名为{K1,V1}。然后,它使用键和值执行 map 函数,并发出一对新的 {K2,V2}进程收集结果并等待所有工作人员完成其工作。完成所有工作后,主服务器启动工作人员发出的对{K2,List[V2]}上的 reduce 部分。这部分可以并行执行或不执行,它用于将所有结果组合成单个输出。请注意,List[V2]是因为工作人员针对单个K2密钥发出的值可能超过一个。
  2. 从我上面提到的第三方模块:

    %% Input = [{K1, V1}]
    %% Map(K1, V1, Emit) -> Emit a stream of {K2,V2} tuples
    %% Reduce(K2, List[V2], Emit) -> Emit a stream of {K2,V2} tuples
    %% Returns a Map[K2,List[V2]]
    

    如果我们研究Erlangs&#39; lists函数, map 部分实际上与执行lists:map/2相等,而reduce部分在某种程度上类似于lists:foldl/3lists:foldr/3它们之间的组合是:lists:mapfoldl/3lists:mapfoldr/3

    1. 如果您正在使用这组mapreduce使用键和值集,那么如果这就是您的意思,则无需进行特殊同步。你只需要等待所有工人完成他们的工作。
    2. 我建议你回顾我上面提到的第三方模块。另请参阅this example。如您所见,您需要定义的唯一内容是MapReduce函数。