2017-10-06 109 views
1

是否有可能在模块module1的函数funct1中产生一个进程p,向模块1的函数funct2中的p发送消息并等待函数funct2中的p的回复,而不必产卵因此被认为是self()的f2?如果是这样,那么执行等待部分的最佳方式是什么?你可以看到下面的代码,以概述我在找什么。 在此先感谢。等待一个进程的回复Erlang

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

回答

5

答案

是。

真正的问题

你混为一谈三个概念:

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

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

当人们对调用上下文感到困惑时,最多的是编写模块接口函数。为了简单起见,大多数模块都是以一种只定义一个进程的方式编写的。没有规定要求这样做,但是很容易理解一个模块以这种方式写入时的功能。接口函数被导出并可供任何进程调用 - 并且它们在调用它们的进程的上下文中调用,而不是在被生成为“是该模块的实例”的进程的上下文中并运行服务循环在此定义。

虽然没有什么陷阱该模块内的一个进程“内”。我可以编写一对模块,一个定义狮子的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>} 

一个独立的例子

现在考虑乒乓服务的这个escript。注意里面只有一个种说话者,它知道如何处理target,pingpong消息。

#! /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之前到达。

这是在运行时会发生什么:

[email protected]:~/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过程的三角形。产生奇怪事物的谈话者的随机电路。一旦你搞砸了,这会很有意义。

+0

非常感谢您提供如此详细的答案。它真的帮助了我。我打算实现的等待过程是由gen_server调用支持的过程。因此,我没有产生一个过程来保存一些数据,并自己实现一个等待机制,我终于不得不实现一个gen_server。 –