【问题标题】:how to spawn or call a process from multiple processes in erlang如何从erlang中的多个进程产生或调用一个进程
【发布时间】:2021-03-31 20:47:55
【问题描述】:

我想从多个函数中生成一个进程并创建一个指向该进程的链接。如何在系统进程中将接收到的信号转换为消息。我想创建“paralleljoin”系统进程,在它从其链接进程(taskTwo,taskThree,TaskFour)接收到('EXIT',pid,normal)消息后,它将产生另一个进程(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 business 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 business logic  of task two \n"),
        link(whereis(join2)),
        exit(whereis(join2), normal).
    taskThree() ->
        io:format(" code for business logic  of task three \n"),
        link(whereis(join2)),
        exit(whereis(join2), normal).
    taskFour() ->
        io:format(" code for business 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 business logic of task five \n").

【问题讨论】:

    标签: erlang


    【解决方案1】:

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

    2) 如果您查看erlang:process_flag/2 的定义,它会说:

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

    返回标志的旧值。

    另见 exit/2。

    这告诉您,如果一个进程正在捕获出口,该进程将收到{'EXIT', From, Reason} 形式的消息。但是,它没有定义From 是什么类型,也没有定义Reason 是什么类型。 From 是一个原子吗?文档确实说“另请参阅 exit/2”,如果您查看文档中的 erlang:exit/2,它会说:

    如果 Pid 正在捕获出口,出口信号将转换为 消息{'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) -> Msg

    Dest 可以是远程或本地进程标识符 [即一个PID],一个 (本地)端口,本地注册名称 [即一个原子],或一个元组 {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
    

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

    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.
    

    【讨论】:

    • 抱歉缩进策略并感谢您的建议,实际上我正在使用eclipse。 task2、task3 和 task4 是 parallelsplit() 下进程的注册名称,但它们没有注册我不知道有什么问题以及如何做到这一点以便与 {'EXIT'、pid、why} 进行比较?我试图传递终止进程的 pid,如 {'EXIT', whereis(task2), normal} 但不工作。
    【解决方案2】:

    使用 OTP,主管。

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

    【讨论】:

      【解决方案3】:

      我尝试传递已终止进程的 pid,例如 {'EXIT', whereis(task2), normal} 但没有工作。

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

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

      模式不能包含表达式:

      模式匹配

      模式 = 表达式

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

      Erlang 编程(Cesarini、Thompson)

      【讨论】:

      • 它说进程中的错误 退出值:{{case_clause,{nil,nil,nil}},即未定义的代码案例 get(messagesreceived) -> put(收到的消息,{nil,nil,nil}); {Task2, Task3, Task4} -> spawn(model, taskFive, [])end,总是 nill 它不会产生 taskFIve
      • case_clause 错误意味着您的案例无法处理所有可能性。要查看实际导致错误的消息,请添加 catch all case:Any -> io:format("X=", [Any])。我不清楚为什么来自崩溃进程的消息会匹配 undefined{Task2, Task3, Task4}
      • 好的,谢谢。如果我没有错过这个概念,我的想法是进程paralleljoin()必须等待所有消息从进程(taskTwo(),taskThree()和taskFour)到达,并且在收到所有消息后它必须触发进程taskfive() . link(pid) 和 exit/2 是否足以在 paralleljoin() 中捕获消息 {'EXIT', From, normal} 消息?
      【解决方案4】:

      我的代码有什么问题

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

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

      1. 每次调用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 business 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 business logic  of task two \n"),
          link(whereis(somename) ).  %% whereis() returns the pid of a registered name
      

      【讨论】:

      • 好的,非常感谢,现在我明白了如何创建到普通进程的链接了。但是我想问一个系统进程,当系统进程接收到错误信号时,信号被转换为{'EXIT', Pid, Why} 形式的消息。如果进程没有错误终止,那么“为什么”将是原子“正常”在我的情况下我想让公共进程“paralleljoin”一个系统进程并添加内置函数 process_flag(trap_exit, true) 来改变进入系统进程,但这对我不起作用。查看代码我已经添加了完整的源代码。
      • @Kiros,我在另一个答案中回复了。
      猜你喜欢
      • 2012-04-06
      • 2012-01-10
      • 1970-01-01
      • 2017-06-13
      • 1970-01-01
      • 2013-11-22
      • 1970-01-01
      • 1970-01-01
      • 2018-06-23
      相关资源
      最近更新 更多