用Erlang求解建环问题

 代码如下所示

  1 -module(ring).
  2 -behaviour(gen_server).
  3 -export([start/3, ring_item_init/1]).
  4 
  5 %% 行为模式gen_server的回调函数
  6 -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
  7 
  8 -define(SERVER, ?MODULE).
  9 
 10 %% 环中各个节点进程的状态,id:标识,从1开始,next:下一个进程号
 11 -record(state, {id, next}).
 12 
 13 %% 监督进程的状态,process_counter:环中节点的数量
 14 -record(server_state, {process_counter}).
 15 
 16 %% 入口函数,M:循环发送消息的次数,N:环中节点的数量,Message:循环的消息内容
 17 start(M, N, Message) ->
 18     gen_server:start_link({local, ?SERVER}, ?MODULE, #server_state{process_counter=N}, []),
 19     create_ring(N),             % 创建环
 20     transmit_msg(Message, M),   % 在环中循环传递消息
 21     destroy_ring().             % 删除环
 22 
 23 create_ring(N) ->
 24     gen_server:call(?SERVER, {create_ring, N}).
 25 
 26 transmit_msg(Message, M) ->
 27     gen_server:call(?SERVER, {transmit_msg, Message, M}).
 28 
 29 destroy_ring() ->
 30     gen_server:call(?SERVER, destroy_ring).
 31 
 32 init(State) ->
 33     process_flag(trap_exit, true),
 34     {ok, State}.
 35 
 36 handle_call(Request, From, State) ->
 37     case Request of
 38         {create_ring, N} ->
 39             create_ring_impl(1, undefined, N);
 40         {transmit_msg, Message, M} ->
 41             transmit_msg_impl(Message, M * State#server_state.process_counter);
 42         destroy_ring ->
 43             header ! {quit, State#server_state.process_counter};
 44         _ -> ok
 45     end,
 46     {reply, From, State}.
 47 
 48 %% 创建环的算法实现 Id:新节点进程的标识,Pre:上一个进程的Pid,
 49 %% Max:环中节点的数量
 50 
 51 %% 创建头节点
 52 create_ring_impl(1, undefined, Max) ->
 53     Pid = spawn_link(?MODULE, ring_item_init, [#state{id=1}]),
 54     register(header, Pid),
 55     create_ring_impl(2, Pid, Max);
 56 %% 创建尾节点
 57 create_ring_impl(Id, Pre, Id) ->
 58     Pid = spawn_link(?MODULE, ring_item_init, [#state{id=Id}]),
 59     Pre ! {next, Pid},
 60     Pid ! {next, header};
 61 %% 创建中间节点
 62 create_ring_impl(Id, Pre, Max) ->
 63     Pid = spawn_link(?MODULE, ring_item_init, [#state{id=Id}]),
 64     Pre ! {next, Pid},
 65     create_ring_impl(Id+1, Pid, Max).
 66 
 67 transmit_msg_impl(Message, MsgTransmitCounter) ->
 68     header ! {msg, Message, MsgTransmitCounter},
 69     receive
 70         complete -> io:format("circle complete~n");
 71         _ -> ok
 72     end.
 73 
 74 %% 节点进程的初始化函数    
 75 ring_item_init(#state{id=Id}=State) ->
 76     io:format("process ~p is created~n", [Id]),
 77     loop(State),
 78     ok.
 79 
 80 %% 节点进程的循环处理函数
 81 loop(#state{id=Id}=State) ->
 82     receive
 83         {next, Next} ->        % 设置当前节点进程链接的下一节点 
 84             io:format("process ~p pid=~p , next is ~p~n", [Id, self(), Next]),
 85             loop(State#state{next=Next});
 86         {msg, Message, MsgTransmitCounter} ->     % 向下一节点传递消息
 87             io:format("process ~p receive ~p, MsgTransmitCounter:~p~n", [Id, Message, MsgTransmitCounter]),
 88             case MsgTransmitCounter-1 =:= 0 of
 89                 true -> 
 90                     ?SERVER ! complete,
 91                     loop(State);
 92                 false ->
 93                     State#state.next ! {msg, Message, MsgTransmitCounter-1},
 94                     loop(State)
 95             end;
 96         {quit, MsgTransmitCounter} ->        % 当前节点进程退出并向下一节点发送退出消息
 97             io:format("~p exit~n", [State#state.id]),
 98             case MsgTransmitCounter-1 =:= 0 of
 99                 true -> exit(normal);
100                 false ->
101                     State#state.next ! {quit, MsgTransmitCounter-1},
102                     exit(normal)
103             end
104     end.
105     
106 handle_cast(Request, State) ->
107     {noreply, State}.
108 
109 handle_info(Info, State) ->
110     {noreply, State}.
111 
112 terminate(Reason, State) ->
113     ok.
114 
115 code_change(OldVsn, State, Extra) ->
116     {ok, State}.

编程注意事项:进程名可注册为原子,其它进程可向该原子发送信息,但如果进程退出,向原子发送信息会弹出bagarg的exit错误

上善若水
原文地址:https://www.cnblogs.com/netbuddy/p/2817762.html