有没有办法在Erlang

时间:2017-11-03 22:18:49

标签: erlang

我的模块中有变量,并且有接收方法来更新变量值。并且多个进程同时调用此方法。我需要在一个进程修改它时锁定此变量。样本如下

mytest.erl

%%%-------------------------------------------------------------------
-module(mytest).

%% API
-export([start_link/0,display/1,callDisplay/2]).

start_link()->
  Pid=spawn(mytest,display,["Hello"]),
  Pid.

display(Val) ->
  io:format("It started: ~p",[Val]),
  NextVal=
    receive
      {call,Msg}->
        NewVal=Val++" "++Msg++" ",
        NewVal;
      stop->
        true
    end,
  display(NextVal).

callDisplay(Pid,Val)->
  Pid!{call,Val}.

启动它

Pid=mytest:start_link().

两个进程同时调用它

P1=spawn(mytest,callDisplay,[Pid,"Walter"]),
P2=spawn(mytest,callDisplay,[Pid,"Dave"]).

我希望它可以添加" Walter"," Dave"喜欢" Hello Walter Dave"然而,当他们中的太多人一起跑时,一些名字(Walter,Dave等)将被覆盖。

因为当P1,P2同时开始时,Val都是"你好"。 P1添加" Walter"成为" Hello Walter",P2 add" Dave"成为"你好戴夫"。 P1首先将它保存到NextVal,然后将其保存到NextVal,然后将其保存为#34; Hello Dave",结果将是" Hello Dave"。 "你好沃尔特"取而代之的是" Hello Dave"和" Walter"永远失去了。

我有什么方法可以锁定" Val"所以当我们添加" Walter"," Dave"会等到值设置完成吗?

1 个答案:

答案 0 :(得分:3)

即使这是一个古老的问题,但值得解释。 从你说的话,如果我是正确的,  你希望看到

  

"您好Walter"和" Hello Dave" 。但是,您已经看到连续的名字被附加到前者," Hello Walter Dave .."

这种行为是正常的,让我们看一下Ernel内存模型。 Erlang进程内存分为三个主要部分:

过程控制块(PCB):   这将保存进程pid,注册名称,表,状态和指向其队列中消息的指针。

<强>堆栈:    这个保存函数参数,局部变量和函数返回地址。

私有堆:这会保留传入的消息复合数据,如元组,列表和二进制(不超过64个字节)。

这些内存中的所有数据都属于拥有进程并且是私有的。

第1阶段:

当调用Pid=spawn(mytest,display,["Hello"])时,创建服务器进程,然后使用&#34; Hello&#34;作为参数传递被调用。由于display/1在服务进程中执行,因此"Hello"参数存在于服务器的进程堆栈中。执行display/1一直持续到receive子句然后阻止并等待与您的格式匹配的消息。

第2阶段:

现在 P1 启动,执行ServerPid ! {call, "Walter"},然后 P2 执行ServerPid ! {call, "Dave"}。在这两种情况下,erlang都会复制邮件并将其发送到服务器的进程邮箱(私有堆)。邮箱中复制的邮件属于服务器进程而不是客户端进程。 现在,当{call, "Walter"}匹配时,Msg会绑定到"Walter"。 从 stage1 开始,我们知道Val"Hello"Newval有界,然后受限于"Val ++ " " ++ Msg" = "Hello Walter"

此时, P2&#39> 消息{call, "Dave"}仍在服务器的邮箱中,等待下一个receive子句发生在下一次递归调用display/1NextVal绑定到NewVal,并以dispaly/1递归调用"Hello Walter"作为参数传递。这会产生第一个打印"Hello Walter ",它现在也存在于服务器的进程堆栈中。

现在再次触及receive子句时, P2&#39> 消息{call, "Dave"}匹配。 现在NewValNextVal绑定到"Hello Walter" ++ " " ++ "Dave" = "Hello Walter Dave".这个作为参数传递给display/1作为新Val打印Hello Walter Dave。简而言之,此变量在每个服务器循环上都会更新。它的作用与gen_server行为中的State术语相同。在您的情况下,连续的客户端调用只是将消息附加到此服务状态变量。现在回答你的问题,

  

我有什么方法可以锁定 Val,所以当我们添加"Walter"时,"Dave"会一直等到值设置完成?

否。不是通过锁定。 Erlang不会这样工作。
没有过程锁定结构,因为它不需要。 数据(变量)始终是不可变的和私有的(除了保留在共享堆中的大型二进制文件之外)到创建它的进程。 此外,它不是您在接收过程处理的Pid ! Msg构造中使用的实际消息。它是复制品。 Val函数中的display/1参数是私有的,属于服务器进程,因为它存在于堆栈内存中,因为每次调用display/1都是由服务器进程本身。因此,任何其他进程都无法锁定甚至看不到该变量。

是。通过顺序消息处理 这正是服务器进程正在做的事情。从队列中一次轮询一条消息。当{call, "Walter"}被采用时,{call, "Dave"}正在队列中等待。您看到意外问候语的原因是您更改了服务器状态,display/1参数用于下一个display/1调用哪个进程{call, "Dave"}