在这个程序中,我不能为我的生活弄清楚如何在一个过程中访问计数器的值。
-module(counter).
-export([start/0,loop/1,increment/1,value/1,stop/1]).
%% First the interface functions.
start() ->
spawn(counter, loop, [0]).
increment(Counter) ->
Counter ! increment.
value(Counter) ->
Counter ! {self(),value},
receive
{Counter,Value} ->
Value
end.
stop(Counter) ->
Counter ! stop.
%% The counter loop.
loop(Val) ->
receive
increment ->
loop(Val + 1);
{From,value} ->
From ! {self(),Val},
loop(Val);
stop -> % No recursive call here
true;
Other -> % All other messages
loop(Val)
end.
我认为是:
{From,value} ->
From ! {self(),Val},
loop(Val);
返回计数器的值,但每次我使用PID ! {PID,value}
或类似的东西时,它都返回!
之后的东西,例如{<0.57.0>, value}
。
答案 0 :(得分:3)
您不应该明确使用!
运算符,它被视为反模式。你可能会遇到一些原子中的拼写错误或一些不良数据的问题,就像你这次做的那样。
为了确保与您的正确沟通,通常会创建一些包装函数来处理正确的数据格式并与进程通信。功能就像increment/1
value/1
和stop/1
一样。事实上,如果你使用那些,你会得到预期的结果;在您的情况下,假设PID
是您的计数器,只需致电counter:value(PID).
你似乎没有什么事情有点不对劲。
首先,!
会向另一个进程发送消息。这就是它的全部。由于Erlang中的所有内容都是表达式(需要返回某些内容,有值),因此每次调用!
都会返回!
的右侧。 PID ! ok.
将返回ok
,无论如何(它有可能失败,但不允许去那里)。你发送信息,继续你的生活或执行。
然而,在收到您的消息后,某些进程可能会决定向您发送一些消息。在{From, value}
的情况下,如果increment
它将不会。如果您希望收到消息,则需要等待它并从您的邮箱中检索它。 receive
子句将同时执行等待和检索。因此,如果您决定自己使用!
,则应使用receive
使用正确的模式匹配。您可以看到value/1
函数就是这样做的。
第三件事是正确使用进程ID。我猜你正确启动了你的计数器,你有Pid
,你可以用!
向它发送消息。但是,如果您希望它发回一些东西,它需要知道您的进程ID,如果您愿意,还需要知道您的地址。所以你应该打电话给PID ! {MyPid, values}
。如何获得MyPid
?使用self()
功能。再次,就像在value/1
函数中一样。
最后一件事很多人在乞讨时出错了。 counter
模块只是一个包含一些函数的文件,它不是整个actor / process,而且它不是一个对象。事实上,其中实现了一些value/1
和stop/1
,但这并不意味着它们将在反作用者/进程上运行。它们就像其他任何函数一样,如果你调用它们,它们将在你的actor /进程中,在你的堆栈上进行评估(同样是从shell调用它们,shell只是另一个actor)。你可以spawn
新进程并告诉它运行loop/1
函数,但就是这样。所有increment/1
value/1
和stop/1
来电都将“在您身边”执行。
如果这有点令人困惑,试着想象计数器模块中的一些更简单的功能,比如
add(A, B) ->
A + B.
即使没有启动任何计数器进程,您也可以从shell执行它。它将在您的进程中创建,在您的堆栈上,它将添加两个数字并返回结果。
这很重要,因为当您致电counter:value(Counter).
时,它会在您的流程上执行Counter ! {self(),value},
“,因此self()
将返回您的流程的Pid,而不是Pid计数器。
理论上你不需要知道这个,如果你只是使用那些包装函数(如果你愿意的话,可以使用API或接口),但是因为你正在学习Erlang,我猜你很快就会编写这样的包装器。了解发生的事情至关重要。请记住,模块中没有魔法,没有秘密绑定或特殊执行。这些只是简单的旧功能,它们的行为就像任何其他语言一样。只有spawn
,receive
和!
才会有所不同。