好几位同学一直不用状态机,什么都是gen_sever,有的说是不熟悉,有的说是有必要用吗?其实erlang的状态机gen_fsm也是由gen_server实现的,这点看gen_fsm.erl就很清楚了。关键是我们要在什么情况下来使用状态机呢,根据个人经验:如果一件事情有很明显的多个状态(阶段),那么使用状态机。比如:电商那边的淡季,旺季,遇到各种日子的打折促销活动;游戏里面的战斗,副本,活动等等,都推荐使用状态机。会给我们带来很多便利,减少程序的逻辑复杂程度,提高可读性。

   先给出gen_fsm的基本行为行为和对应的回调函数,最好在心里有个基本概念:erlang的gen_fsm是一个基于事件驱动的有限状态机。理解了这个基本概念后,就便于理解相关的程序了。

 
  
  1. gen_fsm module                    Callback module 
  2. --------------                    --------------- 
  3. gen_fsm:start_link                -----> Module:init/1 
  4.  
  5. gen_fsm:send_event                -----> Module:StateName/2 
  6.  
  7. gen_fsm:send_all_state_event      -----> Module:handle_event/3 
  8.  
  9. gen_fsm:sync_send_event           -----> Module:StateName/3 
  10.  
  11. gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4 
  12.  
  13. -                                 -----> Module:handle_info/3 
  14.  
  15. -                                 -----> Module:terminate/3 
  16.  
  17. -                                 -----> Module:code_change/4 

    来看一个实际的例子。一个机器可以安排任务,根据任务数量的多少分为busy(忙碌)和idel(空闲)状态,只有在空闲状态下,才允许给机器增加新的任务;只有在忙碌状态下,才允许给机器删减任务。就这样很简单的一个应用,我们用状态机来做:

 
  
  1. %%---------------------------------------------------- 
  2. %% @doc 状态机演示 
  3. %%      <div>处于繁忙状态时,不可以再向其增加新任务;处于空闲状态时,不可以删减其任务。<div> 
  4. %% @author bloodiron888@gmail.com 
  5. %%---------------------------------------------------- 
  6. -module(demo). 
  7. -behaviour(gen_fsm). 
  8.  
  9. %% 外部api 
  10. -export
  11.     [ 
  12.         start_link/0 
  13.         ,stop/0 
  14.         ,add_task/1 
  15.         ,delete_task/1 
  16.     ] 
  17. ). 
  18.  
  19. %% fsm回调函数 
  20. -export([init/1, handle_event/3, handle_sync_event/4, handle_info/3, terminate/3, code_change/4]). 
  21.  
  22. -define(LIMIT, 2). 
  23. -record(state, { 
  24.         task = [] %% [{TaskName, Heavy} | T] 
  25.     }). 
  26.  
  27. %%====================================== 
  28. %% 外部api 
  29. %%====================================== 
  30. %% @spec start_link() -> {ok, Pid::pid()} 
  31. %% @doc 启动状态机 
  32. start_link()-> 
  33.     gen_fsm:start_link({local, ?MODULE}, ?MODULE, [], []). 
  34.  
  35. stop() -> 
  36.     gen_fsm:send_all_state_event(?MODULE, stop). 
  37.  
  38. %% @spec add_task(TaskName) -> {ok, NewTask} | {error, Reason} 
  39. %% @type TaskName = atom(), NewTask = term() 
  40. %% @doc 增加一个任务--只能在空闲状态下增加任务 
  41. add_task({TaskName, Heavy}) -> 
  42.     gen_fsm:sync_send_all_state_event(?MODULE, {add_task, TaskName, Heavy}). 
  43.  
  44. %% @spec delete_task(TaskName) -> {ok, NewTask} | {error, Reason} 
  45. %% @type TaskName = atom(), NewTask = term() 
  46. %% @doc 删除一个任务--只能在繁忙状态下删减任务 
  47. delete_task(TaskName) -> 
  48.     gen_fsm:sync_send_all_state_event(?MODULE, {delete_task, TaskName}). 
  49.  
  50. %%======================================= 
  51. %% FSM回调函数 
  52. %%======================================= 
  53. init([])-> 
  54.     {ok, idel, #state{}}. 
  55.  
  56. handle_event(stop, _StateName, State) -> 
  57.     {stop, normal, State}; 
  58. handle_event(_Event, StateName, State) -> 
  59.     {next_state, StateName, State}. 
  60.  
  61. %% 增加一个任务 
  62. handle_sync_event({add_task, TaskName, Heavy}, _From, idel, State = #state{task = Task}) -> 
  63.     NewTask = case lists:keyfind(TaskName, 1, Task) of 
  64.         {TaskName, _} -> 
  65.             Task1 = lists:keydelete(TaskName, 1, Task), 
  66.             [{TaskName, Heavy} | Task1]; 
  67.         false -> 
  68.             [{TaskName, Heavy} | Task] 
  69.     end, 
  70.     StateName = case length(NewTask) >= ?LIMIT of 
  71.         true -> busy; 
  72.         false -> idel 
  73.     end, 
  74.     {reply, {ok, NewTask}, StateName, State#state{task = NewTask}}; 
  75. handle_sync_event({add_task, _TaskName, _Heavy}, _From, StateName, State) -> 
  76.     {reply, {error, too_busy}, StateName, State}; 
  77.  
  78. %% 删减一个任务 
  79. handle_sync_event({delete_task, TaskName}, _From, busy, State = #state{task = Task}) -> 
  80.     NewTask = lists:keydelete(TaskName, 1, Task), 
  81.     StateName = case length(NewTask) < ?LIMIT of 
  82.         true -> idel; 
  83.         false -> busy 
  84.     end, 
  85.     {reply, {ok, NewTask}, StateName, State#state{task = NewTask}}; 
  86. handle_sync_event({delete_task, _TaskName}, _From, StateName, State) -> 
  87.     {reply, {error, too_idel}, StateName, State}; 
  88.  
  89. handle_sync_event(_Event, _From, StateName, State) -> 
  90.     Reply = ok, 
  91.     {reply, Reply, StateName, State}. 
  92.  
  93. handle_info(_Info, StateName, State) -> 
  94.     {next_state, StateName, State}. 
  95.  
  96. terminate(_Reason, _StateName, _State) -> 
  97.     ok. 
  98.  
  99. code_change(_OldVsn, StateName, State, _Extra) -> 
  100.     {ok, StateName, State}. 

感兴趣的同学可以自己去编译运行一次。这里我们运用了不同状态处理不同的事件。如果觉得不满足的话,接下来我们在最后加入状态函数,这里只是简单的演示,实际的应用都可根据这种方法去扩展和改变,下面是完整的代码:

 
  
  1. %%---------------------------------------------------- 
  2. %% @doc 状态机演示 
  3. %%      <div>处于繁忙状态时,不可以再向其增加新任务;处于空闲状态时,不可以删减其任务。<div> 
  4. %% @author bloodiron888@gmail.com 
  5. %%---------------------------------------------------- 
  6. -module(demo). 
  7. -behaviour(gen_fsm). 
  8.  
  9. %% 外部api 
  10. -export
  11.     [ 
  12.         start_link/0 
  13.         ,stop/0 
  14.         ,add_task/1 
  15.         ,delete_task/1 
  16.         ,info/0 
  17.     ] 
  18. ). 
  19.  
  20. %% 状态函数 
  21. -export([ 
  22.         idel/3 
  23.         ,busy/3 
  24.     ]). 
  25.  
  26. %% fsm回调函数 
  27. -export([init/1, handle_event/3, handle_sync_event/4, handle_info/3, terminate/3, code_change/4]). 
  28.  
  29. -define(LIMIT, 2). 
  30. -record(state, { 
  31.         task = [] %% [{TaskName, Heavy} | T] 
  32.     }). 
  33.  
  34. %%====================================== 
  35. %% 外部api 
  36. %%====================================== 
  37. %% @spec start_link() -> {ok, Pid::pid()} 
  38. %% @doc 启动状态机 
  39. start_link()-> 
  40.     gen_fsm:start_link({local, ?MODULE}, ?MODULE, [], []). 
  41.  
  42. stop() -> 
  43.     gen_fsm:send_all_state_event(?MODULE, stop). 
  44.  
  45. %% @spec add_task(TaskName) -> {ok, NewTask} | {error, Reason} 
  46. %% @type TaskName = atom(), NewTask = term() 
  47. %% @doc 增加一个任务--只能在空闲状态下增加任务 
  48. add_task({TaskName, Heavy}) -> 
  49.     gen_fsm:sync_send_all_state_event(?MODULE, {add_task, TaskName, Heavy}). 
  50.  
  51. %% @spec delete_task(TaskName) -> {ok, NewTask} | {error, Reason} 
  52. %% @type TaskName = atom(), NewTask = term() 
  53. %% @doc 删除一个任务--只能在繁忙状态下删减任务 
  54. delete_task(TaskName) -> 
  55.     gen_fsm:sync_send_all_state_event(?MODULE, {delete_task, TaskName}). 
  56.  
  57. %% @spec info() -> {ok, Info} 
  58. %% @doc 查看当前状态 
  59. info() -> 
  60.     gen_fsm:sync_send_event(?MODULE, info). 
  61. %%======================================= 
  62. %% FSM回调函数 
  63. %%======================================= 
  64. init([])-> 
  65.     {ok, idel, #state{}}. 
  66.  
  67. handle_event(stop, _StateName, State) -> 
  68.     {stop, normal, State}; 
  69. handle_event(_Event, StateName, State) -> 
  70.     {next_state, StateName, State}. 
  71.  
  72. %% 增加一个任务 
  73. handle_sync_event({add_task, TaskName, Heavy}, _From, idel, State = #state{task = Task}) -> 
  74.     NewTask = case lists:keyfind(TaskName, 1, Task) of 
  75.         {TaskName, _} -> 
  76.             Task1 = lists:keydelete(TaskName, 1, Task), 
  77.             [{TaskName, Heavy} | Task1]; 
  78.         false -> 
  79.             [{TaskName, Heavy} | Task] 
  80.     end, 
  81.     StateName = case length(NewTask) >= ?LIMIT of 
  82.         true -> busy; 
  83.         false -> idel 
  84.     end, 
  85.     {reply, {ok, NewTask}, StateName, State#state{task = NewTask}}; 
  86. handle_sync_event({add_task, _TaskName, _Heavy}, _From, StateName, State) -> 
  87.     {reply, {error, too_busy}, StateName, State}; 
  88.  
  89. %% 删减一个任务 
  90. handle_sync_event({delete_task, TaskName}, _From, busy, State = #state{task = Task}) -> 
  91.     NewTask = lists:keydelete(TaskName, 1, Task), 
  92.     StateName = case length(NewTask) < ?LIMIT of 
  93.         true -> idel; 
  94.         false -> busy 
  95.     end, 
  96.     {reply, {ok, NewTask}, StateName, State#state{task = NewTask}}; 
  97. handle_sync_event({delete_task, _TaskName}, _From, StateName, State) -> 
  98.     {reply, {error, too_idel}, StateName, State}; 
  99.  
  100. handle_sync_event(_Event, _From, StateName, State) -> 
  101.     Reply = ok, 
  102.     {reply, Reply, StateName, State}. 
  103.  
  104. handle_info(_Info, StateName, State) -> 
  105.     {next_state, StateName, State}. 
  106.  
  107. terminate(_Reason, _StateName, _State) -> 
  108.     ok. 
  109.  
  110. code_change(_OldVsn, StateName, State, _Extra) -> 
  111.     {ok, StateName, State}. 
  112.  
  113. %%=================================== 
  114. %% 状态函数 
  115. %%=================================== 
  116. idel(info, _From, State) -> 
  117.     {reply, {ok, idel}, idel, State}. 
  118. busy(info, _From, State) -> 
  119.     {reply, {ok, busy}, busy, State}. 

    最后需要记得,状态的划分太多和太少都不利于程序的编写和阅读。