erlang四大behaviour之二-gen_fsm
今天介绍erlang的一个非常重要的behaviour,就是gen_fsm-有限状态机,有限状态机的作用非常之多,比如文本解析,模式匹配、游戏逻辑等等方面的处理都是它的强项,所以这个behaviour非常之重要
1. 有限状态机
有限状态机可以用下面这个公式来表达
State(S) x Event(E) -> Actions(A), State(S')
表示的就是在S状态时如果有事件E发生,那么执行动作A后把状态调整到S’。理解很好理解,如果能够熟练应用必须得下苦功,多练习。
2. 一个例子
erlang手册中用这个例子来解释的:开锁问题,有一个密码锁的门,它就可以看作一个状态机,初始状态门是锁着的,任何时候有人按一个密码键就会产生一 个事件,这个键值和前面的按键组合后与密码相比较,看是否正确,如果输入的密码顺序是对的,那么将门打开30秒,如果输入密码不完全,则等待下次按钮按 下,如果输入密码顺序是错的,则重新开始等待按键按下。
-module(code_lock).
-behaviour(gen_fsm).
-export([start_link/1]).
-export([button/1]).
-export([init/1, locked/2, open/2]).
start_link(Code) ->
gen_fsm:start_link({local, code_lock}, code_lock, Code, []).
button(Digit) ->
gen_fsm:send_event(code_lock, {button, Digit}).
init(Code) ->
{ok, locked, {[], Code}}.
locked({button, Digit}, {SoFar, Code}) ->
case [Digit|SoFar] of
Code ->
do_unlock(),
{next_state, open, {[], Code}, 3000};
Incomplete when length(Incomplete)<length(Code) ->
{next_state, locked, {Incomplete, Code}};
_Wrong ->
{next_state, locked, {[], Code}}
end.
open(timeout, State) ->
do_lock(),
{next_state, locked, State}.
这些代码下面解释
3. 启动状态机
在上一节提到的例子里,我们使用code_lock:start_link(Code)启动gen_fsm
start_link(Code) ->
gen_fsm:start_link({local, code_lock}, code_lock, Code, []).
start_link调用gen_fsm:start_link/4,启动一个新的gen_fsm进程并连接。
1)第一个参数{local, code_lock}指定名字,在本地注册为code_lock
2)第二个参数code_lock是回调模块
3)第三个参数Code是传递给回调模块init函数的参数,就是密码锁的密码
4)第四个[]是状态机的选项
如果进程注册成功,则新的gen_fsm进程调用code_lock:init(Code),返回{ok, StateName,
StateData}。StateName是gen_fsm的初始状态,在这里返回的是locked,表示初始状态下门是锁着的,StateData是
gen_fsm的内部状态,在这里Statedata是当前的按键顺序(初始时为空)和正确的锁代码,是个列表
init(Code) ->
{ok, locked, {[], Code}}.
注意gen_fsm:start_link是同步的,直到gen_fsm进程初始化并准备好开始接受请求时才会返回。加入gen_fsm是监控树的一部 分,那么gen_fsm:start_link必须被使用,也就是被一个监控者调用,gen_fsm:start则是启动单独的gen_fsm进程,也就 是gen_fsm不是监控树的一部分
4. 事件通知
使用gen_fsm:send_event/2来实现按建事件的通知
button(Digit) ->
gen_fsm:send_event(code_lock, {button, Digit}).
code_lock是gen_fsm的名字,且必须用这个名字启动进程,{button, Digit}是发送的事件,事件是作为消息发送给gen_fsm的,当事件被接收到,gen_fsm就调用StateName(Event, StateData),它的返回值应该是{next_state, StateName1, StateData1}。StateName是当前状态名称,而StateName1是将转换到的下一状态名称,StateData1是 StateData的新值
locked({button, Digit}, {SoFar, Code}) ->
case [Digit|SoFar] of
Code ->
do_unlock(),
{next_state, open, {[], Code}, 30000};
Incomplete when length(Incomplete)<length(Code) ->
{next_state, locked, {Incomplete, Code}};
_Wrong ->
{next_state, locked, {[], Code}};
end.
open(timeout, State) ->
do_lock(),
{next_state, locked, State}.
假如门是锁着的且按了一个按键,完整的按键序列和密码相比较,根据比较结果来决定门是打开(状态切到open)还是保持locked状态。
5 超时
假如输入的密码正确,门被打开,locked/2函数返回下面的序列
{next_state, open, {[], Code}, 30000};
30000表示超时30000毫秒,在30秒后,超时发生,调用StateName(timeout, StateData) ,门又重新锁上
open(timeout, State) ->
do_lock(),
{next_state, locked, State}.
6. 所有状态事件
有时候一个事件可以到达gen_fsm进程的任何状态,取代用gen_fsm:send_event/2发送消息和写一段每个状态函数处理事件的代码,这 个消息我们可以用gen_fsm:send_all_state_event/2 发送,用Module:handle_event/3处理
-module(code_lock).
-export([stop/0]).
stop() ->
gen_fsm:send_all_state_event(code_lock, stop).
handle_event(stop, _StateName, StateData) ->
{stop, normal, StateData}.
7. 停止
假如gen_fsm是监控树的一部分,则不需要停止方法,gen_fsm会自动被监控者停止。如果需要在结束前清理数据,那么shutdown strategy必须为一个timeout,并且必须在gen_fsm的init方法里设置捕获exit信号,然后
gen_fsm进程会调用callback方法terminate(shutdown, StateName, StateData)
init(Args) ->
...,
process_flag(trap_exit, true),
...,
{ok, StateName, StateData}.
terminate(shutdown, StateName, StateData) ->
..code for cleaning up here..
ok.
8. 独立gen_fsm进程
加入gen_fsm不是监控树的一部分,stop函数可能有用,如:
-export([stop/0]).
stop() ->
gen_fsm:send_all_state_event(code_lock, stop).
handle_event(stop, _StateName, StateData) ->
{stop, normal, StateData}.
terminate(normal, _StateName, _StateData) ->
ok.
回调函数处理stop事件并返回{stop, normal, StateData1},normal表示正常停止,StateData1为gen_fsm的新的StateData值,这将导致gen_fsm调用 terminate(normal, StateName, StateData1)然后自然的停止
9. 处理其他信息
收到的其他消息由handle_info(Info, StateName, StateData)处理,其他消息的一个例子就是exit消息,假如gen_fsm进程与其他进程link了并且trace了信号,就要处理exit消息
handle_info({'EXIT', Pid, Reason}, StateName, StateData) ->
..code to handle exits here..
{next_state, StateName1, StateData1}.
补充(2012/08/30):
fsm就是Finite State Machines的缩写.知道全称后就明白这个模块到底是干什么的了,原来是状态机,而且是叫有限状态机.
CALLBACK函数
gen_fsm需要导出一些callback函数,每个callback被触发的时机如下:
|
gen_fsm module |
Callback module |
|
-------------- |
--------------- |
|
gen_fsm:start_link |
-----> Module:init/1 |
|
gen_fsm:send_event |
-----> Module:StateName/2 |
|
gen_fsm:send_all_state_event |
-----> Module:handle_event/3 |
|
gen_fsm:sync_send_event |
-----> Module:StateName/3 |
|
gen_fsm:sync_send_all_state_event |
-----> Module:handle_sync_event/4 |
|
-- |
-----> Module:handle_info/3 |
|
- |
-----> Module:terminate/3 |
|
-- |
-----> Module:code_change/4 |
如果callback函数执行异常,或者返回了无效的值,gen_fsm进程将被终止。
具体每个callback返回值类型,参考STDLIB Reference Manual相关章节: http://www.erlang.org/doc/man/gen_fsm.html#Module:init-1
erlang四大behaviour之二-gen_fsm的更多相关文章
- erlang四大behaviour之四-supervisor
http://www.cnblogs.com/puputu/articles/1689621.html 1. 监督规则 一个监督者负责启动.停止.监控他的子进程.监督者的一个基本概念就是当必要的时候重 ...
- erlang四大behaviour之一gen_server
来源:http://www.cnblogs.com/puputu/articles/1701017.html erlang程序设计里面有个设计原则就是把你的进程构造成树,把共用代码提出来,特定功能 ...
- erlang四大behaviour之一gen_server(转载)
erlang程序设计里面有个设计原则就是把你的进程构造成树,把共用代码提出来,特定功能用自己的module实现,这也就是behaviour了,应用behaviour可以减少与本身事务无关的代码量,设计 ...
- erlang四大behaviour之三-gen_event
来源:http://www.cnblogs.com/puputu/articles/1689623.html 1. 事件处理规则 在OTP中,事件管理器是一个事件可以发送到的命名对象,一个事件可以是一 ...
- Android 四大组件之二(Service)
service可以在和多场合的应用中使用,比如播放多媒体的时候用户启动了其他Activity这个时候程序要在后台继续播放,比如检测SD卡上文件的变化,再或者在后台记录你地理信息位置的改变等等,总之服务 ...
- 11、四大组件之二-Service高级(二)Native Service
一.Service的分类 1.1>Android Service 使用Java编写在JVM中运行的服务 1.2>Native Service 使用C/C++完成的服务,一般在系统开始时完成 ...
- 7、四大组件之二-Service高级
一.Native Service 1>什么是Native Service 使用JNI编写,在系统启动完成之前启动的系统级服务. 2>哪些服务是Native Service ACCESSIB ...
- 6、四大组件之二-Service初步
一.什么是Service 有些用时比较长得操作我们希望他在后台运行 ,不耽误我们当前的操作 . 这就引入了Service概念 . 常见的比如:访问网络 , 文件IO操作 , 大数据的数据库任务,播放音 ...
- VFS四大对象之二 struct inode
继上一篇文章:http://www.cnblogs.com/linhaostudy/p/7427027.html 二.inode结构体:(转自http://blog.csdn.net/shanshan ...
随机推荐
- AsyncHttpClient 中的重定向和 setEnableRedirects 方法异常解决
今天使用 AsyncHttpClient 开源库,遇到个很崩溃的问题: 方法 setEnableRedirects(false); 从名称上看应该是重定向开关的方法,设置为 false 后则普通请 ...
- 单向链表仿LinkedList
public class ChainTable { private Node firstNode;//第一个节点 private Node lastNode;//最后一个节点 private int ...
- 2016NOMS全国运营峰会——史上更强嘉宾阵容提前揭晓!
参加2016NOMS全国运营峰会的演讲嘉宾来自运营领域的各个方面,包括用户运营.内容运营.活动运营.数据运营等.自大会消息一出立刻受到业界的广泛关注,并吸引了众多业内人士踊跃报名.日前,这一运营界峰会 ...
- 【git】TurtoiseGit使用手册
A,创建本地分支 操作:Create Branch ->输入本地分支名 -> OK B,创建本地分支和远程分支的映射 操作: 1,拉取远程分支:Fetch -> 选中Arbitray ...
- svn添加新文件自动忽略
背景:做项目,用的客户端TortoiseSVN1.8,发现新建的文件,不是问号(?),而是自动忽略的减号,提交的时候也确实没有,说明不是符号混乱,确实是被忽略了,网上找了解决方案记录如下: 查看svn ...
- mysql count max min 语句用法
count 用法 求总条数 $sql="select count(*) as total from e_user"; $query = mysql_query($sql, $lin ...
- WI-FI: connection through CLI
First, ensure that the computer/sbc has WI-FI parts. install wpa_supplicant, edit /etc/wpa_supplican ...
- Installation LEK Cluster
The structure of cluster like this: We used four VM: A 10.32.xxx.213 ubuntu12.04 B 10.32.xxx.214 ...
- 使用Session防止表单重复提交
本文出处:http://www.cnblogs.com/xdp-gacl/p/3859416.html 在平时开发中,如果网速比较慢的情况下,用户提交表单后,发现服务器半天都没有响应,那么用户可能会以 ...
- Oracle杀死死锁进程
查杀系统死锁的sql,最近想改造成存储过程,如下: CREATE OR REPLACE PROCEDURE HERO_KILLLOCKSESSION (OUT_COUNT OUT NUMBER, OU ...