Reputation: 49
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
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
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