如何在流程中访问计数器的值?

时间:2017-10-29 19:19:10

标签: erlang

在这个程序中,我不能为我的生活弄清楚如何在一个过程中访问计数器的值。

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

1 个答案:

答案 0 :(得分:3)

TL; DR

您不应该明确使用!运算符,它被视为反模式。你可能会遇到一些原子中的拼写错误或一些不良数据的问题,就像你这次做的那样。

为了确保与您的正确沟通,通常会创建一些包装函数来处理正确的数据格式并与进程通信。功能就像increment/1 value/1stop/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/1stop/1,但这并不意味着它们将在反作用者/进程上运行。它们就像其他任何函数一样,如果你调用它们,它们将在你的actor /进程中,在你的堆栈上进行评估(同样是从shell调用它们,shell只是另一个actor)。你可以spawn新进程并告诉它运行loop/1函数,但就是这样。所有increment/1 value/1stop/1来电都将“在您身边”执行。

如果这有点令人困惑,试着想象计数器模块中的一些更简单的功能,比如

add(A, B) -> 
  A + B.

即使没有启动任何计数器进程,您也可以从shell执行它。它将在您的进程中创建,在您的堆栈上,它将添加两个数字并返回结果。

这很重要,因为当您致电counter:value(Counter).时,它会在您的流程上执行Counter ! {self(),value},“,因此self()将返回您的流程的Pid,而不是Pid计数器。

理论上你不需要知道这个,如果你只是使用那些包装函数(如果你愿意的话,可以使用API​​或接口),但是因为你正在学习Erlang,我猜你很快就会编写这样的包装器。了解发生的事情至关重要。请记住,模块中没有魔法,没有秘密绑定或特殊执行。这些只是简单的旧功能,它们的行为就像任何其他语言一样。只有spawnreceive!才会有所不同。