以下代码片段由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()时,如何对上述完全功能进行编码以避免潜在的竞争条件?
答案 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.
我还没跑过。