technion
technion

Reputation: 49

Properly managing Erlang concurrency

I'm putting together some Erlang code with the following process:

- Spawn a number of threads
- Each thread scans an IP address
- Report back to the main thread the results

I've got the main thread running this:

cve_receive() ->
    receive
    Msg ->
            io:fwrite("~s~n", [Msg]),
            cve_receive()
    after 5000 ->
            ok
    end.

It's functionally correct and receives what it's meant to receive, but I'm not really sure how to properly manage the exit condition. It currently just sits on a timeout, but the reality is that all threads usually finish in 100ms or so, which means there's a pointless timeout in this picture. Really what it should say is "after all threads terminate".

Is there an idiomatic way of doing this?

Upvotes: 0

Views: 79

Answers (2)

Derek Brown
Derek Brown

Reputation: 511

To make @johlo's answer a bit more idiomatic:

cve_receive(0) ->
  all_done;
cve_receive(N) ->
  receive
    Msg ->
       io:fwrite("~s~n", [Msg]),
       cve_receive(N-1)
    after 5000 ->
      {timeout, N}
  end.

Upvotes: 1

johlo
johlo

Reputation: 5500

The simplest solution would be to create a barrier by keeping a counter in cve_receive so you can keep track of how many worker processes that have completed.

Something like:

%% First Spawn N work processes
...
%% Then receive N messages.
cve_receive(N).
...
cve_receive(N) ->
    receive
        Msg ->
           io:fwrite("~s~n", [Msg]),
           case N == 1 of
              true  -> all_done;
              false -> cve_receive(N-1)
           end;
        after 5000 ->
           {timeout, N}
    end.        

If you need to perform some cleanup in the case processes don't finish within the timeout interval you could store a list of process id's instead of the counter. Remove the pid on receiving the message and on timeout you can e.g. kill all the processes remaining in the list.

EDIT: If worker processes crash the simple solution above would still work, since you would get a timeout eventually. But you can use the monitor/2 function and handle the DOWN message in your cve_receive function.

Upvotes: 1

Related Questions