在Erlang中同时生成1000个进程

6tdlim6h  于 2022-12-08  发布在  Erlang
关注(0)|答案(1)|浏览(182)

我想在Erlang中生成1000个或可变数量的进程。
server.erl:

-module(server).
-export([start/2]).

start(LeadingZeroes, InputString) ->
    % io:format("Leading Zeroes: ~w", [LeadingZeroes]),
    % io:format("InputString: ~p", [InputString]).
    mineCoins(LeadingZeroes, InputString, 100).

mineCoins(LeadingZeroes, InputString, Target) ->
    PID = spawn(miner, findTargetHash(), []), % How to spawn this process 1000 times so that each process computes something and sends the results here
    PID ! {self(), {mine, LeadingZeroes, InputString, Target}},
    receive
        {found, Number} ->
            io:fwrite("Rectangle area: ~w", [Number]);
        % {square, Area} ->
        %     io:fwrite("Square area: ~w", [Area]);
        Other ->
            io:fwrite("In Other!")
    end.
    % io:fwrite("Yolo: ~w", [Square_Area]).

Miner.erl(客户端):

-module(miner).
-export([findTargetHash/0]).

findTargetHash() ->
    receive
         {From , {mine, LeadingZeroes, InputString, Target}} ->
            % do something here
            From ! {found, Number};
        {From, {else, X}} ->
            io:fwrite("In Else area"),
            From ! {square, X*X}
    end,
    findTargetHash().

在这里,我希望产生1000个进程(矿工),如何实现这一点?通过列表解析或递归或任何其他方式?

a11xaf1n

a11xaf1n1#

一般来说,你可以这样做N次:

-module(a).
-compile(export_all).

go(0) ->
    io:format("!finished!~n");
go(N) ->
    io:format("Doing something: ~w~n", [N]),
    go(N-1).

在 shell 中:

3> c(a).   
a.erl:2:2: Warning: export_all flag enabled - all functions will be exported
%    2| -compile(export_all).
%     |  ^
{ok,a}

4> a:go(3).
Doing something: 3
Doing something: 2
Doing something: 1
!finished!
ok

如果你需要启动N个进程,并随后向它们发送消息,那么你需要它们的pid来完成这一操作,所以你必须把它们的pid保存在某个地方:

go(0, Pids) ->
    io:format("All workers have been started.~n"),
    Pids;
go(N, Pids) ->
    Pid = spawn(b, worker, [self()]),
    go(N-1, [Pid|Pids]).

-module(b).
-compile(export_all).

worker(From) ->
    receive
        {From, Data} ->
            io:format("Worker ~w received ~w.~n", [self(), Data]), 
            From ! {self(), Data * 3};
        Other ->
            io:format("Error, received ~w.~n", [Other])
    end.

要启动N=3工作进程,可以如下调用go/2

Pids = a:go(3, []).

对于没有编写代码的人来说,这有点尴尬:为什么我必须传递一个空的列表呢?所以,你可以像这样定义一个go/1

go(N) ->  go(N, []).

然后,只需编写以下命令即可启动3个辅助进程:

Pids = go(3).

接下来,您需要向每个工作进程发送一条消息,其中包含它们需要执行的工作:

do_work([Pid|Pids], [Data|Datum]) ->
    Pid ! {self(), Data},
    do_work(Pids, Datum);
do_work([], []) ->
    io:format("All workers have been sent their work.~n").

最后,您需要从工作者那里收集结果:

gather_results([Worker|Workers], Results) ->
    receive
        {Worker, Result} ->
            gather_results(Workers, [Result|Results])
    end;
gather_results([], Results) ->
    Results.

关于gather_results/2,有几点需要注意:

  1. receive中的Worker变量已经在函数头中被赋值,因此receive不是在等待任何工作进程发送消息,而是在等待特定的工作进程发送消息。
  2. Workers列表中的第一个Worker进程可能是运行时间最长的进程,您可以在receive中等待(比如说)10分钟,让该进程完成,但从其他工作进程获取结果将不需要等待。收集所有结果将基本上花费最长的进程加上几微秒的时间来循环通过其他进程。类似地,对于列表中最长和最短进程的其它排序,接收所有结果将仅花费等于最长进程加上几微秒的时间。
    下面是在shell中运行的测试:
27> c(a).                                                       
a.erl:2:2: Warning: export_all flag enabled - all functions will be exported
%    2| -compile(export_all).
%     |  ^

{ok,a}

28> c(b).                                                       
b.erl:2:2: Warning: export_all flag enabled - all functions will be exported
%    2| -compile(export_all).
%     |  ^

{ok,b}

29> Pids = a:go(3, []).                                         
All workers have been started.
[<0.176.0>,<0.175.0>,<0.174.0>]

30> a:do_work(Pids, [1, 2, 3]).                                 
All workers have been sent their work.
Worker <0.176.0> received 1.
Worker <0.175.0> received 2.
Worker <0.174.0> received 3.
ok

31> a:gather_results(Pids, []).                                 
[9,6,3]

相关问题