有没有办法在Erlang中的不同进程之间锁定变量

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了有没有办法在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"]).

我希望它可以像“Hello Walter Dave”一样逐一添加“Walter”,“Dave”,然而,当它们中太多的一起运行时,一些名字(Walter,Dave等)将被覆盖。

因为当P1,P2同时启动时,Val都是“Hello”。 P1加“Walter”成为“Hello Walter”,P2加“Dave”成为“Hello Dave”。 P1首先将它保存为NextVal为“Hello Walter”,然后P2将其保存为NextVal为“Hello Dave”,因此结果为“Hello Dave”。 “Hello Walter”被“Hello Dave”取代,“Walter”永远失去了。

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

答案

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

“你好沃尔特”和“你好戴夫”。但是,你看到连续的名字被附加到前者,“Hello Walter Dave ..”

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

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

Stack:这个保存函数参数,局部变量和函数返回地址。

私有堆:它保存传入的消息复合数据,如元组,列表和二进制(不大于64字节)。

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

阶段1:

调用Pid=spawn(mytest,display,["Hello"])时,将创建服务器进程,然后调用以“Hello”作为参数传递的显示函数。由于display/1在服务进程中执行,因此"Hello"参数存在于服务器的进程堆栈中。继续执行display/1,直到它到达receive子句然后阻止并等待与您的格式匹配的消息。

第二阶段:

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

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

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

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

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

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

以上是关于有没有办法在Erlang中的不同进程之间锁定变量的主要内容,如果未能解决你的问题,请参考以下文章

有没有办法在 Erlang 中使用进程创建二叉树?

有没有办法浏览 erlang 术语存储 (ETS)

我怎么知道这是由 erlang 中的主管重新启动我的进程的最后一个周期

Java中的进程间文件锁定

Unity中的环境变量在开发和生产之间有所不同

如何验证erlang进程是否已休眠?