等待进程 Erlang 的回复
Wait on the reply of a process Erlang
是否可以在模块 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.
答案
是的。
真正的问题
您混淆了三个概念:
- 进程(谁是
self()
,什么是pid()
)
- 函数
- 模块
进程是有生命的。进程有自己的内存space。这些进程是调用的东西。这是唯一真正重要的身份。当您考虑 "who is self()
in this case" 时,您实际上是在问 "what is the calling context?" 如果我生成一个进程的两个实例,它们可能会在生命中的某个时刻调用相同的函数——但这些调用的上下文是 完全不同,因为进程有自己的生活和记忆space。 Victor 和 Victoria 同时跳绳并不能证明他们是同一个人
人们最容易混淆调用上下文的地方是在编写 模块接口函数 时。为了简单起见,大多数模块都以仅定义单个进程的方式编写。没有强制要求这样做的规则,但是当模块以这种方式编写时,很容易理解模块的作用。接口函数被导出并可供任何进程调用——它们在调用它们的进程的上下文中调用, 而不是 在生成到 "be an instance of that module" 的进程的上下文中调用和运行其中定义的服务循环。
不过,没有捕获进程"within"那个模块。我可以编写一对模块,一个定义狮子的 AI,另一个定义鲨鱼的 AI,让一个进程在执行过程中本质上转换身份——但这几乎总是一个非常糟糕的主意(因为它变得混乱)。
函数就是函数。他们就是这样。模块由函数组成。无话可说。
如何等待消息
我们使用 receive
构造等待消息。它匹配接收到的消息(它始终是 Erlang 术语)并根据消息的形状 and/or 内容选择要执行的操作。
请仔细阅读以下内容:
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>}
一个独立的例子
现在考虑乒乓服务的脚本。请注意,内部只定义了 一种 类型的 talker,它知道如何处理 target
、ping
和 pong
消息。
#! /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
的使用,因为消息发送 如此之快 以至于它会超过对 [= 的调用速度21=] 会减慢实际的 talker
进程并导致一种奇怪的情况,即退出消息(通常)在两个谈话者之间的 ping 和 pong 之前到达。
这是 运行 时发生的情况:
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 进程三角形。产生一个随机的谈话者电路,做奇怪的事情。一旦你把它搞砸了,这就会突然变得有意义。
是否可以在模块 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.
答案
是的。
真正的问题
您混淆了三个概念:
- 进程(谁是
self()
,什么是pid()
) - 函数
- 模块
进程是有生命的。进程有自己的内存space。这些进程是调用的东西。这是唯一真正重要的身份。当您考虑 "who is self()
in this case" 时,您实际上是在问 "what is the calling context?" 如果我生成一个进程的两个实例,它们可能会在生命中的某个时刻调用相同的函数——但这些调用的上下文是 完全不同,因为进程有自己的生活和记忆space。 Victor 和 Victoria 同时跳绳并不能证明他们是同一个人
人们最容易混淆调用上下文的地方是在编写 模块接口函数 时。为了简单起见,大多数模块都以仅定义单个进程的方式编写。没有强制要求这样做的规则,但是当模块以这种方式编写时,很容易理解模块的作用。接口函数被导出并可供任何进程调用——它们在调用它们的进程的上下文中调用, 而不是 在生成到 "be an instance of that module" 的进程的上下文中调用和运行其中定义的服务循环。
不过,没有捕获进程"within"那个模块。我可以编写一对模块,一个定义狮子的 AI,另一个定义鲨鱼的 AI,让一个进程在执行过程中本质上转换身份——但这几乎总是一个非常糟糕的主意(因为它变得混乱)。
函数就是函数。他们就是这样。模块由函数组成。无话可说。
如何等待消息
我们使用 receive
构造等待消息。它匹配接收到的消息(它始终是 Erlang 术语)并根据消息的形状 and/or 内容选择要执行的操作。
请仔细阅读以下内容:
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>}
一个独立的例子
现在考虑乒乓服务的脚本。请注意,内部只定义了 一种 类型的 talker,它知道如何处理 target
、ping
和 pong
消息。
#! /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
的使用,因为消息发送 如此之快 以至于它会超过对 [= 的调用速度21=] 会减慢实际的 talker
进程并导致一种奇怪的情况,即退出消息(通常)在两个谈话者之间的 ping 和 pong 之前到达。
这是 运行 时发生的情况:
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 进程三角形。产生一个随机的谈话者电路,做奇怪的事情。一旦你把它搞砸了,这就会突然变得有意义。