几天以来,我正在学习Erlang,使用Joe Armstrong的伟大着作Programming Erlang。
我正在关注并发编程这一章,在本章的最后,还有一个问题需要解决:
编写函数start(AnAtom,Fun)将AnAtom注册为spawn(Fun)。 在两个并行的情况下,确保您的程序正常工作 进程同时评估start / 2。在这种情况下,你必须 保证其中一个过程成功而另一个失败。
我解决了这个问题的第一部分,确保每个进程都会生成一个不同的原子。
关于第二部分,我被卡住了,因为如果我理解正确,我找不到检查两个并行进程是否同时评估函数start/2
的方法。起初,我想过一个计数器检查进程产生的数量,但我无法理解如何执行此检查。我在this thread找到了一些有趣的东西(来自OOP背景,我考虑过Singleton模式),但我不确定用计数器读取和写入文件是否是执行此检查的最佳方法。我认为有一种简单的方法可以做到这一点。
我写的代码如下:
-module(probl).
-export([start/2,stop/1]).
start(Atom,Fun) ->
% check here counter value
case isUniqueProcess(Times) of % here I'm trying to use a counter, but I'm stuck..
true ->
Pid = spawn(fun() -> doSomething(Atom,Fun) end),
register(Atom,Pid);
false ->
io:format("Sorry, no more processes to spawn."),
void
end.
stop(Atom) ->
case whereis(Atom) of
undefined -> void;
Pid -> io:format("Stopped."), Pid ! stop
end.
isUniqueProcess(Times) -> % here I should increment the counter, but how?
if
Times =:= 0 ->
true;
Times > 0 ->
false
end.
doSomething(Atom,Fun) ->
receive
stop -> void
after 15000 ->
Fun(),
doSomething(Atom,Fun)
end.
对我来说,来自强大的OOP背景,转换另一种编程思维并不是那么容易,就像它在Erlang中发生的那样,也许这就是我遇到困难的原因。我该如何解决这个问题?
答案 0 :(得分:2)
您可以尝试使用此代码。
start(Atom, Fun) when is_atom(Atom), is_function(Fun, 0) ->
Sender = self(),
Fun2 = fun() ->
case catch register(Atom, self()) of
true ->
Sender ! {started, self()},
Fun();
_ ->
Sender ! {already_running, self()}
end
end,
Pid = spawn(Fun2),
receive
{started, Pid} ->
{ok, Pid};
{already_running, Pid} ->
already_running
end.
答案 1 :(得分:2)
如果两个或更多进程尝试以相同的名称注册,那么只有第一个进程才会成功。以下将在尝试注册时生成错误。在这种情况下,仅仅让后续进程崩溃是不够的?为此,必须在已启动的过程中完成注册。
如果您不想让进程崩溃,可以将进程包装在catch中并终止并使用exit(normal)
显式终止它。
答案 2 :(得分:1)
使用rvirding方法:
start(AnAtom, Fun) ->
spawn(fun() -> register(AnAtom, self()), Fun() end).
或者:
start(AnAtom, Fun) ->
spawn(fun() -> case catch register(AnAtom, self()) of true -> Fun(); _ -> ok end end).