Erlang 避免竞争条件

Erlang 避免竞争条件,erlang,Erlang,以下代码片段由Francesco Cesarini和Simon Thompson在《Erlang编程》一书的第112页拍摄,作为Erlang中可能存在的竞争条件的说明 start() -> case whereis(db_server) of undefined -> Pid = spawn(db_server, init, []), register(db_server, Pid), {ok, Pid}; Pid wh

以下代码片段由Francesco Cesarini和Simon Thompson在《Erlang编程》一书的第112页拍摄,作为Erlang中可能存在的竞争条件的说明

start() -> 
  case whereis(db_server) of
    undefined -> 
      Pid = spawn(db_server, init, []), 
      register(db_server, Pid), 
      {ok, Pid};
    Pid when is_pid(Pid) ->
      {error, already_started}
  end.
在不逐字复制详细信息的情况下,作者解释说,如果两个进程同时执行start(),那么运行“undefined”部分的进程1可能无法完成,因为进程2导致它被抢占。然后,进程2将运行“未定义”部分以完成。现在,当进程1恢复时,进程2已经注册了db_服务器,导致对register()的its调用抛出运行时错误。我希望你能理解我的意思,因为我不想窃取这本书的文本


我的问题是,当两个进程同时执行start()时,如何对上述精确的功能进行编码以避免潜在的争用情况?

您可以使用a来序列化请求。

通常,这是通过让派生的进程注册自己的名称来解决的,然后向其父级发回响应,告知其是否成功

start() ->
   Pid = spawn(db_server, init, [self()]),
   receive {Pid, StartResult} ->
       StartResult
   end.

init(Parent) ->
    try register(db_server, self()) of
        true ->
            Parent ! {ok, started},
            real_init()
    catch error:_ ->
        Parent ! {error, already_started}
    end.
(可能无法编译或工作。未经检查在此处键入。:)


您可以在gen.erl中找到仔细实现的版本。实际上,在实际代码中,您只需使用OTP行为来重用该版本。

您希望启动多少台服务器?您最初的问题暗示了一个,而@cthulahops的评论则暗示了两个,一个服务器和一个备份。对于两台服务器,您可以尝试以下操作:

start() ->
    case whereis(db_server) of
        undefined ->
            Spid = spawn(db_server, init, []),
            %% In race condition there can be only one that succeeds to register
            case catch register(db_server, Spid) of
                true -> {ok,Spid};             %We are it
                {error,_} ->                   %Server registered, register as backup
                    register(db_server_backup, Spid),
                    {ok,Spid}
            end;
        _ ->                                   %Server registered, start backup
            Bpid = spawn(db_server, init, []),
            register(db_server_backup, Bpid),
            {ok,Bpid}
    end.

不过我还没有运行过。

我知道OTP为这些类型的问题提供了可靠的解决方案,但我对非OTP“设计模式”感兴趣。感谢您的回复。我的问题是,我想使用问题中的start/0,而不是您建议的start/1。例如,我想调用start/0两次,一次调用生成db_服务器,一次调用生成db_备份服务器(如果db_服务器已经注册)。谢谢您的回复。我只是让代码比您的代码稍微通用一点。已编辑为仅启动/0。我刚刚发现您实际上希望启动两台服务器,而不是失败。您可以编辑catch块来注册db_backup_服务器,如果调用了三次,请记住处理错误。这似乎是一个尴尬的设计选择,但对我来说,我宁愿明确开始twoMe太!不幸的是,我受到API的限制,它只允许我为主服务器和备份服务器提供start/0。不过,你重新提出的建议在我看来确实不错。当然,在缺乏正式证据的情况下,我无法证实比赛条件不可能发生。非常感谢您的帮助。我想启动两台服务器。我最初使用O'Reilly书中的例子,因为我认为人们更容易理解,从而回答我的问题。我想启动2台服务器(一台主服务器和一台备份服务器)来提供故障切换。我确实有类似于你建议的代码,但也许你的代码更能避免比赛条件。