我的模块中有变量,并且有接收方法来更新变量值。并且多个进程同时调用此方法。我需要在一个进程修改它时锁定此变量。样本如下
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"会等到值设置完成吗?
答案 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/1
。 NextVal
绑定到NewVal
,并以dispaly/1
递归调用"Hello Walter"
作为参数传递。这会产生第一个打印"Hello Walter "
,它现在也存在于服务器的进程堆栈中。
现在再次触及receive
子句时, P2&#39> 消息{call, "Dave"}
匹配。
现在NewVal
和NextVal
绑定到"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"}