等待进程Erlang的回复

时间:2017-10-06 23:12:30

标签: process erlang wait

是否可以在模块module1的函数funct1中生成进程p,在module1的函数funct2中向p发送消息并等待funct2内的p的回复,而不必生成f2即因此被视为self()?如果是这样,实施等待部分的最佳方法是什么?您可以看到下面的代码,以概述我在寻找什么。 提前谢谢。

-module(module1)
...
funct1(...)
->  

 Pid = spawn(module2, function3, [[my_data]]),
 ...


funct2(...)
->
  ...
  Pid ! {self(), {data1, data2}},

  % wait here for the reply from Pid
  % do something here based on the reply.

1 个答案:

答案 0 :(得分:4)

答案

真正的问题

您正在混淆三个概念:

  • 流程(谁是self(),什么是pid()
  • 功能
  • 模块

过程是一种生物。进程有自己的内存空间。这些过程是调用的东西。这是真正重要的唯一身份。当你想到"在这种情况下谁是self()"你真的在问"什么是呼叫环境?"如果我生成一个进程的两个实例,它们都可能在他们生活中的某个时刻调用相同的函数 - 但这些调用的上下文完全不同,因为这些进程有他们自己的生命和他们自己的生命记忆空间。仅仅因为维克多和维多利亚在同一时间跳绳并不能使他们成为同一个人。

在编写模块接口函数时,人们对调用上下文的看法最为混乱。为简单起见,大多数模块以一种只定义一个过程的方式编写。没有规则要求这样做,但是很容易理解模块以这种方式编写时的作用。接口函数被导出并可供任何进程调用 - 并且它们在调用它们的进程的上下文中调用, not 在生成的进程的上下文中是"是一个实例那个模块"并运行其中定义的服务循环。

陷阱过程""但是那个模块。我可以编写一对模块,一个定义狮子的AI,另一个定义鲨鱼的AI,并且在执行过程中有一个过程实质上切换身份 - 但这几乎总是一个非常糟糕的主意(因为它让人感到困惑)。

功能只是功能。他们就是这样。模块由函数组成。没有什么可说的。

如何等待消息

我们使用receive构造等待消息。它匹配收到的消息(始终是一个Erlang术语),并根据消息的形状和/或内容选择要做的事情。

仔细阅读以下

1> Talker =
1>   fun T() ->
1>     receive
1>       {tell, Pid, Message} ->
1>         ok = io:format("~p: sending ~p message ~p~n", [self(), Pid, Message]),
1>         Pid ! {message, Message, self()},
1>         T();
1>       {message, Message, From} ->
1>         ok = io:format("~p: from ~p received message ~p~n", [self(), From, Message]),
1>         T();
1>       exit ->
1>         exit(normal)
1>     end
1>   end.
#Fun<erl_eval.44.87737649>
2> {Pid1, Ref1} = spawn_monitor(Talker).
{<0.64.0>,#Ref<0.1042362935.2208301058.9128>}
3> {Pid2, Ref2} = spawn_monitor(Talker).
{<0.69.0>,#Ref<0.1042362935.2208301058.9139>}
4> Pid1 ! {tell, Pid2, "A CAPITALIZED MESSAGE! RAAAR!"}.
<0.64.0>: sending <0.69.0> message "A CAPITALIZED MESSAGE! RAAAR!"
{tell,<0.69.0>,"A CAPITALIZED MESSAGE! RAAAR!"}
<0.69.0>: from <0.64.0> received message "A CAPITALIZED MESSAGE! RAAAR!"
5> Pid2 ! {tell, Pid1, "a lower cased message..."}.
<0.69.0>: sending <0.64.0> message "a lower cased message..."
{tell,<0.64.0>,"a lower cased message..."}
<0.64.0>: from <0.69.0> received message "a lower cased message..."
6> Pid1 ! {tell, Pid1, "Sending myself a message!"}.
<0.64.0>: sending <0.64.0> message "Sending myself a message!"
{tell,<0.64.0>,"Sending myself a message!"}
<0.64.0>: from <0.64.0> received message "Sending myself a message!"
7> Pid1 ! {message, "A direct message from the shell", self()}.
<0.64.0>: from <0.67.0> received message "A direct message from the shell"
{message,"A direct message from the shell",<0.67.0>}

独立示例

现在考虑一下乒乓球服务的这个问题。请注意,内部只定义了一种类型的谈话者,它知道如何处理targetpingpong消息。

#! /usr/bin/env escript

-mode(compile).

main([CountString]) ->
    Count = list_to_integer(CountString),
    ok = io:format("~p: Starting pingpong script. Will iterate ~p times.~n", [self(), Count]),
    P1 = spawn_link(fun talker/0),
    P2 = spawn_link(fun talker/0),
    pingpong(Count, P1, P2).


pingpong(Count, P1, P2) when Count > 0 ->
    P1 ! {target, P2},
    P2 ! {target, P1},
    pingpong(Count - 1, P1, P2);
pingpong(_, P1, P2) ->
    _ = erlang:send_after(1000, P1, {exit, self()}),
    _ = erlang:send_after(1000, P2, {exit, self()}),
    wait_for_exit([P1, P2]).


wait_for_exit([]) ->
    ok = io:format("~p: All done, Returing.~n", [self()]),
    halt(0);
wait_for_exit(Pids) ->
    receive
        {exiting, Pid} ->
            ok = io:format("~p: ~p is done.~n", [self(), Pid]),
            NewPids = lists:delete(Pid, Pids),
            wait_for_exit(NewPids)
    end.


talker() ->
    receive
        {target, Pid} ->
            ok = io:format("~p: Sending ping to ~p~n", [self(), Pid]),
            Pid ! {ping, self()},
            talker();
        {ping, From} ->
            ok = io:format("~p: Received ping from ~p. Replying with pong.~n", [self(), From]),
            From ! pong,
            talker();
        pong ->
            ok = io:format("~p: Received pong.~n", [self()]),
            talker();
        {exit, From} ->
            ok = io:format("~p: Received exit message from ~p. Retiring.~n", [self(), From]),
            From ! {exiting, self()}
    end.

有一些细节,例如使用erlang:send_after/3,因为消息发送如此之快它会超过调用io:format/2的速度慢在实际的talker进程中,导致一个奇怪的情况,即退出消息(通常)在两个谈话者之间的ping和pongs之前到达。

以下是运行时会发生的情况:

ceverett@changa:~/Code/erlang$ ./pingpong 2
<0.5.0>: Starting pingpong script. Will iterate 2 times.
<0.61.0>: Sending ping to <0.62.0>
<0.62.0>: Sending ping to <0.61.0>
<0.61.0>: Sending ping to <0.62.0>
<0.62.0>: Sending ping to <0.61.0>
<0.61.0>: Received ping from <0.62.0>. Replying with pong.
<0.62.0>: Received ping from <0.61.0>. Replying with pong.
<0.61.0>: Received ping from <0.62.0>. Replying with pong.
<0.62.0>: Received ping from <0.61.0>. Replying with pong.
<0.61.0>: Received pong.
<0.62.0>: Received pong.
<0.61.0>: Received pong.
<0.62.0>: Received pong.
<0.61.0>: Received exit message from <0.5.0>. Retiring.
<0.62.0>: Received exit message from <0.5.0>. Retiring.
<0.5.0>: <0.61.0> is done.
<0.5.0>: <0.62.0> is done.
<0.5.0>: All done, Returing.

如果您运行它几次(或在繁忙的运行时),某些输出可能会以不同的顺序运行。这只是并发的本质。

如果您是Erlang的新手,上面的代码可能需要一段时间才能沉入其中。请自行使用该pingpong脚本。编辑它。让它做新事物。创建一个ping过程的三角形。产生一个随机的谈话者的电路,做奇怪的事情。一旦你搞砸了它,这将是有意义的。