2

如何在 Erlang 中分叉/克隆一个进程,就像fork在 Unix 中一样?

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

也许用法如下所示:

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 中实现这一点吗?

4

3 回答 3

4

(这个问题也在 erlang-questions 邮件列表中得到了回答。)

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().

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

于 2012-09-06T15:53:21.707 回答
2

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

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

在每个 quant 时间过程中都有一个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}
于 2012-09-06T16:12:07.550 回答
1

Erlang 中没有分叉。但是您可以使用作为 erlang 的 BIF 的 spawn/1、spawn/2、spawn/3、spawn/4(另请参阅 spawn_link)中的一个,请参阅 erlang 模块

因此,例如:

-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

于 2012-09-06T16:03:25.097 回答