Parcourons le code :
-module(ring).
-export([start/2, node/2]).
Le nom node
est l'un de ceux que j'évite parce qu'un node() en Erlang a la connotation d'une VM Erlang fonctionnant sur une machine - généralement plusieurs nodes fonctionnant sur plusieurs machines. Je préfère l'appeler ring_proc
ou quelque chose comme ça.
node(NodeNumber, NumberOfNodes) ->
NextNodeNumber = (NodeNumber + 1) rem NumberOfNodes,
NextNodeName = node_name(NextNodeNumber),
C'est ce que nous essayons de frayer, et nous obtenons un numéro vers le prochain nœud et le nom du prochain nœud. Regardons node_name/1
comme un interlude :
node_name(NodeNumber) ->
list_to_atom(lists:flatten(io_lib:format("node~w", [NodeNumber]))).
Cette fonction est une mauvaise idée. Vous aurez besoin d'un nom local qui doit être un atome, donc vous avez créé une fonction qui peut créer de tels noms arbitraires. L'avertissement ici est que la table des atomes n'est pas ramassée et limitée, donc nous devrions l'éviter si possible. L'astuce pour résoudre ce problème est de passer les pids à la place et de construire l'anneau en sens inverse. Le processus final fera alors le noeud de l'anneau :
mk_ring(N) ->
Pid = spawn(fun() -> ring(none) end),
mk_ring(N, Pid, Pid).
mk_ring(0, NextPid, Initiator) ->
Initiator ! {set_next, NextPid},
Initiator;
mk_ring(N, NextPid, Initiator) ->
Pid = spawn(fun() -> ring(NextPid) end),
mk_ring(N-1, Pid, Initiator).
Et ensuite nous pouvons réécrire votre fonction de départ :
start(NumberOfNodes, NumberOfCircuits) ->
RingStart = mk_ring(NumberOfNodes)
RingStart ! {operate, NumberOfCircuits, self()},
receive
done ->
RingStart ! stop
end,
ok.
Le code Ring est alors quelque chose du genre :
ring(NextPid) ->
receive
{set_next, Pid} ->
ring(Pid);
{operate, N, Who} ->
ring_ping(N, NextPid),
Who ! done,
ring(NextPid);
ping ->
NextPid ! ping,
ring(NextPid);
stop ->
NextPid ! stop,
ok
end.
Et pour tirer quelque chose autour de l'anneau N fois :
ring_ping(0, _Next) -> ok;
ring_ping(N, Next) ->
Next ! ping
receive
ping ->
ring_ping(N-1, Next)
end.
(Au passage, aucun de ces codes n'a été testé, il se peut donc qu'ils soient tout à fait faux).
Quant au reste de votre code :
receive
CircuitNumber ->
io:format("Node ~p Circuit ~p~n", [NodeNumber, CircuitNumber]),
Je marquerais le CircuitNumber
avec un certain atome : {run, CN}
.
LastNode = NodeNumber =:= NumberOfNodes - 1,
NextCircuitNumber = case LastNode of
true ->
CircuitNumber - 1;
false ->
CircuitNumber
end,
Cela peut être fait avec un if :
NextCN = if NodeNumber =:= NumberOfNodes - 1 -> CN -1;
NodeNumber =/= NumberOfNodes - 1 -> CN
end,
La suite ici :
if
NextCircuitNumber > 0 ->
NextNodeName ! NextCircuitNumber;
true ->
ok
end,
if
CircuitNumber > 1 ->
node(NodeNumber, NumberOfNodes);
true ->
ok
end
a besoin de la true
cas, à moins que vous ne l'ayez jamais touché. Le processus s'arrêtera si rien ne correspond dans le champ if
. Il est souvent possible de recâbler le code de manière à ne pas dépendre autant des constructions de comptage, comme le suggère mon code ci-dessus.
Plusieurs problèmes peuvent être évités avec ce code. Un des problèmes avec le code actuel est que si quelque chose s'écrase dans l'anneau, il est cassé. Nous pouvons utiliser spawn_link
plutôt que spawn
pour relier l'anneau ensemble, donc de telles erreurs détruiront l'anneau entier. En outre, notre ring_ping
se plantera si on lui envoie un message pendant que l'anneau fonctionne. Il est possible d'y remédier, le moyen le plus simple étant probablement de modifier l'état du processus de l'anneau de manière à ce qu'il sache qu'il est en cours de fonctionnement et qu'il se replie sur lui-même. ring_ping
en ring
. Enfin, nous devrions probablement aussi lier le spawn initial afin de ne pas nous retrouver avec un grand nombre d'anneaux qui sont vivants mais auxquels personne n'a de référence. Nous pourrions peut-être enregistrer le processus initial afin qu'il soit facile de s'emparer de l'anneau par la suite.
Le site start
est également mauvaise à deux égards. Premièrement, nous devrions utiliser make_ref()
pour étiqueter un message unique et recevoir l'étiquette, de sorte qu'un autre processus ne peut pas être sinistre et envoyer simplement done
au processus de démarrage pendant que l'anneau fonctionne. Nous devrions probablement aussi ajouter un moniteur sur l'anneau, pendant qu'il fonctionne. Sinon, nous ne serons jamais informés, si l'anneau tombe en panne alors que nous attendons la réponse de l'opérateur. done
message (avec étiquette). OTP fait les deux dans ses appels synchrones d'ailleurs.
Enfin, enfin : Non, vous n'avez pas à nettoyer une inscription.