避免竞争条件

时间:2010-12-06 13:26:21

标签: erlang

以下代码片段由Francesco Cesarini和Erlang编程的Simon Thompson撰写,作为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_server,导致其对register()的调用抛出运行时错误。我希望你能理解我的意思,因为我不想读这本书的文字。

我的问题是,当两个进程同时执行start()时,如何对上述完全功能进行编码以避免潜在的竞争条件?

3 个答案:

答案 0 :(得分:9)

通常,这可以通过让生成的进程注册自己的名称来解决,然后向父进程发回一个响应,告诉父进程是否成功。

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行为来重用该版本。

答案 1 :(得分:2)

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

答案 2 :(得分:1)

您想要启动多少台服务器?你的原始问题意味着一个,而对@cthulahoops的评论说两个,一个服务器和一个备份。对于两台服务器,您可以尝试以下方式:

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.

我还没跑过。