Check if parallel processes evaluate same function

2019-07-28 09:39发布

Since few days, I'm learning Erlang, using the great book Programming Erlang by Joe Armstrong.

I'm following the chapter about concurrent programming, and, at the end of chapter, there's a little problem to solve:

Write a function start(AnAtom, Fun) to register AnAtom as spawn(Fun). Make sure your program works correctly in the case when two parallel processes simultaneously evaluate start/2. In this case, you must guarantee that one of these processes succeeds and the other fails.

I solved the first part of this problem, making sure that each process will be spawned with a different atom.

About the second part, I'm stuck because, if I understood correctly, I cannot find a way to check if two parallel processes evaluate simultaneously the function start/2. At first, I thought about a counter checking the number of spawns of the process, but I'm not able to understand how to perform this check. I found something interesting here in this thread (coming from an OOP background, I thought about the Singleton pattern), but I'm not sure if reading and writing a file with a counter is the best way to perform this check. I think there's a simple way to do that.

The code I wrote is below:

-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.

For me, coming from a strong OOP background, it's not so easy to switch another programming mindset, like it happens in Erlang, and maybe this is the reason of my difficulties. How can I solve this problem?

标签: erlang
3条回答
做自己的国王
2楼-- · 2019-07-28 09:58

You can try this code.

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.
查看更多
▲ chillily
3楼-- · 2019-07-28 10:03

Using rvirding approach:

start(AnAtom, Fun) ->
  spawn(fun() -> register(AnAtom, self()), Fun() end).

Or alternatively:

start(AnAtom, Fun) ->
  spawn(fun() -> case catch register(AnAtom, self()) of true -> Fun(); _ -> ok end end).
查看更多
乱世女痞
4楼-- · 2019-07-28 10:21

If both, or more processes, try to register under the same name then only the first one will succeed. The following will generate an error when they try to register. Wouldn't be enough just to let the subsequent processes crash in that case? To do this the registering must be done in the started process itself.

If you don't want to crash the process the you could wrap the process in a catch and terminate and explicitly terminate it using exit(normal).

查看更多
登录 后发表回答