【问题标题】:How to fork/clone a process in Erlang如何在 Erlang 中分叉/克隆进程
【发布时间】:2012-09-06 14:42:05
【问题描述】:

如何在 Erlang 中 fork/clone 一个进程,就像 Unix 中的 fork

我搜索了很多,但一无所获。

也许用法是这样的:

case fork() of
  {parent, Pid} ->
    in_parent_process_now();
  {child, Pid} ->
    in_child_process_now();
  {error, Msg} ->
    report_fork_error(Msg)
end.

有什么想法吗?

编辑:

为了更好地说明我的观点,以下面的C代码为例:

f();
fork();
g();

这里忽略了fork()的返回值,所以父进程和子进程的下一步都是一样的,都是执行g()

我可以在 Erlang 中实现这一点吗?

【问题讨论】:

    标签: process erlang fork


    【解决方案1】:

    (这个问题是also answered in the erlang-questions mailing list。)

    Erlang 没有“分叉”操作。但是它有一个spawn 操作:

    parent_process() ->
      will_be_executed_by_parent_process(),
      spawn(fun() -> will_be_executed_by_child_process() end),
      will_also_be_executed_by_parent_process().
    

    ... 其中函数名称显示它们将在什么上下文中执行。请注意,传递给子进程的任何数据都将复制到新进程的堆中。

    【讨论】:

    • 我认为这与 Unix fork 不同。如果您将spawn 视为fork(我知道它们是不同的,只是说代码中的位置),will_also_be_executed_by_parent_process(),按照Unix fork语义,应该是子进程也执行。
    【解决方案2】:

    如您所知,在 erlang 中有通用模式来实现流程:

    loop( State ) ->
       receive
          Message ->
             NewState = process( Message, State ),
             loop( NewState )
       end.
    

    在每个量化时间过程中都有一个State。因此,如果您想从当前“分叉”某些流程 - 您必须为其传递特定消息。进程必须识别该消息并在生成的进程中使用其当前状态的副本生成新进程。

    我已经创建了示例,以说明上面的文字:

    -module( test ).
    -export( [ fork/1, get_state/1, change_state/2 ] ).
    -export( [ loop/1 ] ).
    
    loop( State ) ->
            receive
                    { fork, Sender } ->
                            %%
                            %% if you want to link with child process
                            %% call spawn_link instead of spawn
                            %%
                            ClonePid = spawn( ?MODULE, loop, [ State ] ),
                            responseTo( Sender, ClonePid ),
                            loop( State );
    
                    { get_state, Sender } ->
                            responseTo( Sender, { curr_state, State } ),
                            loop( State );
    
                    { change_state, Data, Sender } ->
                            { Response, NewState } = processData( Data, State ),
                            responseTo( Sender, Response ),
                            loop( NewState )
            end.
    
    fork( Pid ) ->
            Ref = make_ref(),
            Pid ! { fork, { Ref, self() } },
            get_response( Ref ).
    
    get_state( Pid ) ->
            Ref = make_ref(),
            Pid ! { get_state, { Ref, self() } },
            get_response( Ref ).
    
    change_state( Pid, Data ) ->
            Ref = make_ref(),
            Pid ! { change_state, Data, { Ref, self() } },
            get_response( Ref ).
    
    get_response( Ref ) ->
            receive
                    { Ref, Message } -> Message
            end.
    
    responseTo( { Ref, Pid }, Mes ) ->
            Pid ! { Ref, Mes }.
    
    processData( Data, State ) ->
            %%
            %% here comes logic of processing data
            %% and changing process state
            %%
            NewState = Data,
            Response = { { old_state, State }, { new_state, NewState } },
            { Response, NewState }.
    

    让我们在 erlang shell 中测试它:

    1> c(test).
    {ok,test}
    

    创建初始状态为first_state的父进程

    2> ParentPid = spawn( test, loop, [ first_state ] ).
    <0.38.0>
    3> test:get_state( ParentPid ).
    {curr_state,first_state}
    4> 
    

    让我们将父进程的状态更改为second_state

    4> test:change_state( ParentPid, second_state ).
    {{old_state,first_state},{new_state,second_state}}
    

    从父进程派生新进程:

    5> ChildPid = test:fork( ParentPid ).
    <0.42.0>
    

    检查分叉进程的状态(与父进程相同):

    6> test:get_state( ChildPid ).
    {curr_state,second_state}
    

    【讨论】:

    • 接受这个答案,因为它对我帮助最大。您知道是否有任何方法可以克隆 控制流 以及状态(数据)?
    • @Xiao Jia,我不知道这是否是您真正想要的:但是,据我了解 - 流程控制基于当前的流程状态。因此,在 fork 具有几乎相同状态的子进程之后,您可以向它传递消息,其中告诉他做某事(或做与父进程相同的事情)。所以它模拟了一种流控制的克隆。
    • 从概念上讲没关系。但我正在尝试编写一个第三方实用程序,它实际上可以在向用户隐藏详细信息的同时进行分叉。
    【解决方案3】:

    Erlang 中没有分叉。但是您可以使用 spawn/1、spawn/2、spawn/3、spawn/4(另见 spawn_link)中的一个,它们是 erlang see erlang module 的 BIF。

    所以,例如:

    -module(mymodule).
    -export([parent_fun/0]).
    
    parent_fun() ->
        io:format("this is the parent with pid: ~p~n", [self()]),
        spawn(fun() -> child_fun() end),
        io:format("still in parent process: ~p~n", [self()]).
    
    child_fun() ->
        io:format("this is child process with pid: ~p~n", [self()]).
    

    在erlang shell中执行为:

    mymodule:parent_fun().
    

    注意父进程和子进程有不同的pid。

    我强烈建议你阅读:http://learnyousomeerlang.com/the-hitchhikers-guide-to-concurrency

    【讨论】:

    • 与@aronisstav 的答案相同的问题。 io:format("still in parent process: ~p~n", [self()]) 也应该由子进程执行吧?但是如果你spawn这样的话,控制流是分开的。
    猜你喜欢
    • 2011-12-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-17
    • 2010-09-09
    • 1970-01-01
    • 2022-10-24
    相关资源
    最近更新 更多