+1 to rvirding: " " . - "" - . , :
-module(smokers).
-export([smokers/0]).
smokers() ->
Rounds = 1000000,
Agent = self(),
lists:foreach(fun(Material) -> spawn(fun() -> startSmoker(Agent, Material) end) end, materials()),
done = agent(Rounds),
io:format("Done ~p rounds~n", [Rounds]).
agent(0) ->
done;
agent(Rounds) ->
offer(twoRandomMaterials(), Rounds).
offer(AvailableMaterials, Rounds) ->
receive
{take, Smoker, AvailableMaterials} ->
Smoker ! smoke,
receive
doneSmoking ->
agent(Rounds - 1)
end
end.
startSmoker(Agent, Material) ->
smoker(Agent, lists:delete(Material, materials())).
smoker(Agent, Missing) ->
Agent ! {take, self(), Missing},
receive
smoke ->
Agent ! doneSmoking,
smoker(Agent, Missing)
end.
twoRandomMaterials() ->
Materials = materials(),
deleteAt(random:uniform(length(Materials)) - 1, Materials).
materials() ->
[paper, tobacco, match].
deleteAt(_, []) -> [];
deleteAt(0, [_ | T]) -> T;
deleteAt(Idx, [H | T]) -> [H | deleteAt(Idx - 1, T)].
Interestingly, there are potentially three messages in this queue when we try to use receive {take, Smoker, AvailableMaterials}. However, only one of them can be processed. Then there is an inner handshake receive doneSmoking. Thus, the selection of appropriate messages, allowing the code to do some work at all, and also not to lose other messages takewhen receiving a confirmation message, is what solves all concurrency problems here. If messages that did not match / were not processed were discarded at any time, smokerthey would be stuck forever (if they would not repeat their requests periodically).