Erlang - 具有替代方案的顺序接收语句

时间:2015-12-17 18:33:42

标签: erlang

我在Erlang中编写了一个简单的程序。它的任务是计算给定多项式2 * x ^ 2 + 3 * x + 5的值。并行计算2 * x ^ 2和3 * x两者,最后将这两个部分加在一起,5是添加,最后我们得到了结果。有一个函数计算2 * x ^ 2,第二个计算3 * x,最后一个计算最终结果。这是代码:

-module(count2).
-compile([export_all]).
%-export([f_main/0]).

%computes the value of the polynomial 2*x^2 + 3*x + 5

f_result() ->
    {Arg1, Res_1} = receive
        {f1, X1, Temp1} -> {X1, Temp1}
    end,
    Res_2 = receive
        {f2, _, Temp2} -> Temp2
    end,
    Res = Res_1 + Res_2 + 5, %adds 5 to the result of 2*X^2+3*X
    io:format("f(~p) = ~p~n",[Arg1, Res]),
    f_result().

%when the line 14 was io:format("f(~p) = ~p~p,[X1, Res], the compilation error occured:
%variable 'X1' unsafe in 'receive'


f_2(PidWyn) -> %computes 2*X^2
    receive
        {f2, X} ->
            Res = 2*math:pow(X,2),
            PidWyn ! {f2, X, Res},
            f_2(PidWyn);
        {finish} ->
            io:format("f_2 : finish~n")
end.

f_1(PidWyn) -> %computes 3*X
    receive
        {f1, X} ->
            Res = 3*X,
            PidWyn ! {f1, X, Res},
            f_1(PidWyn);
        {finish} ->
            io:format("f_1 : finish~n")
end.        

f_main() ->
    PidW = spawn(?MODULE, f_result, []), %the process ID of the function that computes and displays the final result
    Pid1 = spawn(?MODULE, f_1, [PidW]),
    Pid2 = spawn(?MODULE, f_2, [PidW]),
    L = [1,2,3,4,5],
    [Pid1 ! {f1, X} || X <- L], %sends a series of messages to the function f_1
    [Pid2 ! {f2, X} || X <- L], %sends a series of messages to the function f_2
    Pid2 ! {finish},
    Pid1 ! {finish},
    PidW ! {finish}, %sends the message to the function f_result to make it stop
    io:format("f_main : finish~n").

如您所见,f_result函数逐个有两个receive语句。第一个从f_1函数接收元组,第二个从f_2函数接收元组。之后,该函数显示结果并调用自身进行循环。

我想让f_result函数接收{finish}元组会告诉它停止工作(类似的解决方案在f_1f_2函数中可见),但我无法正确放置所需receive。我试着把

{Arg1, Res_1} = receive
                 {f1, X1, Temp1} -> {X1, Temp1};
                 {finish} -> io:format("f_result : finish~n"),
                   exit(0)
               end,

但它产生以下输出:

f_main : finish
f_result : finish
f_1 : finish
f_2 : finish
ok

如果你能给我一些建议,我将不胜感激。

2 个答案:

答案 0 :(得分:1)

输出不一定有任何问题。在Erlang中,消息是异步发送的,因此运行f_result的进程可能比运行f_1f_2的进程更早地处理消息。

顺便说一句,代替exit(0),您通常会写exit(normal)来表示流程退出&#34;通常&#34;,而不是因为错误。这个惯例在少数地方依赖。例如,具有非normal退出的进程也会导致链接进程退出,并且使用transient主管策略,当且仅当退出原因不是时,才能使主管重新启动进程normal

答案 1 :(得分:0)

虽然您看到的输出并不一定意味着您的代码实际上存在问题。 Erlang中的所有消息都是异步的,只保留一个promise。从一个进程发送到另一个进程的两条消息如果到达则会以相同的顺序到达。

首先为什么输出没有告诉你任何事情。使用消息执行io模块io:format/1,2,3的IO操作。这意味着您将四个进程中的四条消息发送到io服务器进程。 (在您的情况下,对于组长,默认情况下名为进程user执行标准IO操作。)不保证这些消息按照您期望的顺序到达。所涉及的过程产生四对独立的过程。它们通常以相同的顺序到达,但您不能依赖它。

其次,从finish进程到f_main的{​​{1}}邮件存在类似问题。该消息可能并且可能会在f_resultf_result的结果之前到达f_1进程。这将导致您在提议的解决方案中的第一次接收将提前完成您的f_2

解决方案是强制f_result消息以正确的顺序到达输入。只有当它通过相同的路径,相同的过程对行进时才能完成。您必须通过finishfinish发送到f_result。所以你可以这样做:

f_1

结果:

-module(count2).
-compile([export_all]).
%-export([f_main/0]).

%computes the value of the polynomial 2*x^2 + 3*x + 5

f_result() ->
    {Arg1, Res_1} = receive
                        {f1, X, Temp1} -> {X, Temp1};
                        finish ->
                            io:format("f_result : finish~n"),
                            exit(normal)
                    end,
    Res_2 = receive
                {f2, _, Temp2} -> Temp2
            end,
    Res = Res_1 + Res_2 + 5, %adds 5 to the result of 2*X^2+3*X
    io:format("f(~p) = ~p~n",[Arg1, Res]),
    f_result().

f_2(PidWyn) -> %computes 2*X^2
    receive
        {f2, X} ->
            Res = 2*X*X,
            PidWyn ! {f2, X, Res},
            f_2(PidWyn);
        finish ->
            io:format("f_2 : finish~n")
    end.

f_1(PidWyn) -> %computes 3*X
    receive
        {f1, X} ->
            Res = 3*X,
            PidWyn ! {f1, X, Res},
            f_1(PidWyn);
        finish ->
            PidWyn ! finish,
            io:format("f_1 : finish~n")
    end.        

f_main() ->
    PidW = spawn(?MODULE, f_result, []), %the process ID of the function that computes and displays the final result
    Pid1 = spawn(?MODULE, f_1, [PidW]),
    Pid2 = spawn(?MODULE, f_2, [PidW]),
    L = [1,2,3,4,5],
    [Pid1 ! {f1, X} || X <- L], %sends a series of messages to the function f_1
    [Pid2 ! {f2, X} || X <- L], %sends a series of messages to the function f_2
    Pid2 ! finish,
    Pid1 ! finish,
    io:format("f_main : finish~n").

请注意,只有> count2:f_main(). f_main : finish f_1 : finish f_2 : finish f(1) = 10 f(2) = 19 ok f(3) = 32 f(4) = 49 f(5) = 70 f_result : finish f(X) = Y保证按此顺序显示,因为它们是来自同一f_result : finish进程到f_result io服务器的邮件。 (即使userf_main : finish也可能出现反向顺序,因为shell内部的工作方式,但没有必要知道这个细节。你可能永远不会看到这种情况发生。)