如何在erlang中从多个进程生成或调用进程

时间:2018-03-17 10:55:13

标签: erlang

我想从多个函数中生成一个进程,并创建一个指向该进程的链接。如何在系统进程中将接收到的信号转换为消息。我想制作" paralleljoin"系统进程,并从它的链接进程(taskTwo,taskThree,TaskFour)收到('退出',pid,正常)消息后,它将生成另一个进程(taskFive),但这在我的代码中没有正常工作,即" taskFive"没有执行。

    -module(model).
    -compile(export_all).
    start() ->
        io:format("codes of start event \n"),
        %The spawn method calls the taskOne function with no parameters
        spawn(model, taskOne, []),
        Pid1 = spawn(model, paralleljoin, []),
        register(join2,Pid1 ).
    taskOne() ->
        io:format(" code for bussiness logic  of task one \n"),
        spawn(model, parallelsplit, []).
    parallelsplit()->
        Pid2 =  spawn(model, taskTwo, []),
        register(task2, Pid2),
        Pid3 =  spawn(model, taskThree, []),
        register(task3, Pid3),
        Pid4 = spawn(model, taskFour, []),
        register(task4, Pid4).
    taskTwo() ->
        io:format("code for bussiness logic  of task two \n"),
        link(whereis(join2)),
        exit(whereis(join2), normal).
    taskThree() ->
        io:format(" code for bussiness logic  of task three \n"),
        link(whereis(join2)),
        exit(whereis(join2), normal).
    taskFour() ->
        io:format(" code for bussiness logic of task four \n"),
        link(whereis(join2)),
        exit(whereis(join2), normal).
    paralleljoin()->
        process_flag(trap_exit, true),
        Task2 = whereis(task2),
        Task3 = whereis(task3),
        Task4 = whereis(task4),
        case get(messagesreceived) of
            undefined -> put(messagesreceived, {nil, nil, nil});
            {Task2, Task3, Task4} ->
                spawn(model, taskFive, [])end,
        receive
            {'EXIT', Task2, normal} ->
                put(messagesreceived, setelement(1, get(messagesreceived), Task2));
            {'EXIT', Task3, normal}->
                put(messagesreceived, setelement(2, get(messagesreceived), Task3));
            {'EXIT', Task4, normal}->
                put(messagesreceived, setelement(3, get(messagesreceived), Task4));
            Other ->
                ignore %Do something, or do nothing
         end,
    paralleljoin().
taskFive()->
    io:format("code for bussiness logic of task five \n").

4 个答案:

答案 0 :(得分:0)

  

我的代码出了什么问题

1)没有名为model:paralleljoin()的函数。

  

当我从每个函数中生成它时,它会创建3个不同的进程

2)每次调用spawn()时,erlang都会创建一个新进程。就这么简单。 spawn()不会检查赋值左侧的变量名称,以确定要执行的操作。

如果您希望taskTwo(),taskThree()和taskFour()进程都链接到一个公共进程,那么您需要将公共进程作为参数传递给每个函数,如下所示:

parallelsplit()->   
    Pid = spawn(model, paralleljoin, []),
    spawn(?MODULE, taskTwo, [Pid]),
    spawn(?MODULE, taskThree, [Pid]),
    spawn(?MODULE, taskFour, [Pid]).

taskTwo(ParallelJoin) ->
    io:format("code for bussiness logic  of task two \n"),
    link(ParallelJoin).

...
...

另一种选择是注册公共进程,它创建类似于可以从任何进程访问的全局变量:

start() ->
   io:format("codes of start event \n"),
   register(somename, spawn(model, paralleljoin, []) )
   spawn(model, taskOne, []).
...

taskTwo() ->
    io:format("code for bussiness logic  of task two \n"),
    link(whereis(somename) ).  %% whereis() returns the pid of a registered name

答案 1 :(得分:0)

1)如果代码没有正确缩进,你不能指望任何人查看你的代码。你的缩进策略是什么?每行是否缩进了一个随机数的空格?发布后你是否无法查看代码并发现缩进是一团糟?如果您无法弄清楚如何在计算机编程论坛上发布代码,那么您需要与谷歌进行一些研究。您是否可能没有使用计算机编程编辑器?

2)如果你看一下erlang:process_flag/2的定义,它会说:

  

当trap_exit设置为true时,到达进程的退出信号为   转换为{'EXIT', From, Reason}消息,可以作为   普通的消息。如果trap_exit设置为false,则进程退出if   它接收正常以外的退出信号,退出信号为   传播到其链接的进程。申请流程通常是   不要陷阱退出。

     

返回标志的旧值。

     

另见exit / 2。

这告诉您如果进程正在捕获退出,则该进程将接收{'EXIT', From, Reason}形式的消息。但是,它无法定义From的类型以及Reason的类型。 From是原子吗?文档会说,“另请参阅exit / 2”,如果您查看erlang:exit/2的文档,则会说:

  

如果Pid正在捕获退出,则退出信号将转换为a   消息{'EXIT', From, Reason}并传递给消息队列   PID。 From 是发送退出流程的流程标识符   信号。

这告诉你,From需要成为Pid(但似乎你已经知道了,因为在你的评论中你说退出信号将被转换为{'EXIT', Pid, Why}形式的消息)。

现在让我们看看你的模式是否匹配退出消息:

{'EXIT', task2, normal} ->

'EXIT'原子将在退出消息中与位置1匹配,但对于From,您指定原子task2。原子是Pid吗?提示:阅读我之前发布的代码示例中的单独注释。

请注意,您可以使用注册名称(即原子)发送消息,因为!是erlang:send / 2的简写,在文档中定义如下:

  

erlang:send(Dest,Msg) - >消息

     

Dest可以是远程或本地 进程标识符 [即一个Pid],a   (本地)端口, 本地注册名称 [即一个原子],或一个元组{RegName,   Node}表示另一个节点的注册名称。

但是,通常情况下不能使用注册名称代替Pid。

3)而不是:

receive
    MESSAGE->   
        case MESSAGE of
            {'EXIT', task2, normal} ->
                put(messagesreceived, setelement(1, get(messagesreceived), taskTwo));
            {'EXIT', task3, normal}->
                 put(messagesreceived, setelement(2, get(messagesreceived), taskThree));
            {'EXIT', task4, normal}->
                put(messagesreceived, setelement(3, get(messagesreceived), taskFour))
        end
end

你可以写:

receive
    {'EXIT', task2, normal} ->
        put(messagesreceived, setelement(1, get(messagesreceived), taskTwo));
    {'EXIT', task3, normal}->
         put(messagesreceived, setelement(2, get(messagesreceived), taskThree));
    {'EXIT', task4, normal}->
        put(messagesreceived, setelement(3, get(messagesreceived), taskFour))
end

不同之处在于:对于您的代码,如果您指定的消息之外的消息到达,则会发生运行时错误 - 因为当案例语句不能处理所有可能性时会发生这种情况 - 而在我的示例中如果指定的消息到达,则接收将阻止。您始终可以通过添加catch all来阻止接收阻止:

receive
    {'EXIT', task2, normal} ->
        put(messagesreceived, setelement(1, get(messagesreceived), taskTwo));
    {'EXIT', task3, normal}->
         put(messagesreceived, setelement(2, get(messagesreceived), taskThree));
    {'EXIT', task4, normal}->
        put(messagesreceived, setelement(3, get(messagesreceived), taskFour));
    Other ->
        %Do something, or do nothing
end,
%%No matter what message was sent to this process, 
%%execution will continue here.

答案 2 :(得分:0)

使用OTP,主管。

让一个进程生成并重新启动worker,并将pid传递给需要它的其他进程或使用命名进程,这样你就不需要传递pid。

答案 3 :(得分:0)

  

我试图传递已终止进程的pid,如{'EXIT',whereis(task2),normal}但无效。

您收到illegal pattern错误了吗?如果是这样,你需要写:

Task2 = whereis(task2),
receive
    {'EXIT', Task2, Why} ->

模式不能包含表达式:

  

模式匹配

     

模式=表达式

     

模式由可以包含两个绑定的数据结构组成   和未绑定的变量,以及文字值(如原子,   整数或字符串)   ...
  ...
  表达式由数据结构,绑定变量,数学运算和函数调用组成。它可能不包含未绑定的值。

     

Erlang编程(Cesarini,Thompson)