`
coderplay
  • 浏览: 576794 次
  • 性别: Icon_minigender_1
  • 来自: 广州杭州
社区版块
存档分类
最新评论

OTP设计原则:Gen_Fsm 行为

阅读更多

3 Gen_Fsm 行为

This chapter should be read in conjunction with gen_fsm(3), where all interface functions and callback functions are described in detail.

3.1 Finite State Machines

3.1 有限状态机

A finite state machine, FSM, can be described as a set of relations of the form:
java 代码
  1. State(S) x Event(E) -> Actions(A), State(S')  


These relations are interpreted as meaning:

If we are in state S and the event E occurs, we should perform the actions A and make a transition to the state S'.

For an FSM implemented using the gen_fsm behaviour, the state transition rules are written as a number of Erlang functions which conform to the following convention:

java 代码
 
  1. StateName(Event, StateData) ->  
  2.     .. code for actions here ...  
  3.     {next_state, StateName', StateData'}  


3.2 Example

A door with a code lock could be viewed as an FSM. Initially, the door is locked. Anytime someone presses a button, this generates an event. Depending on what buttons have been pressed before, the sequence so far may be correct, incomplete or wrong.

If it is correct, the door is unlocked for 30 seconds (30000 ms). If it is incomplete, we wait for another button to be pressed. If it is is wrong, we start all over, waiting for a new button sequence.

Implementing the code lock FSM using gen_fsm results in this callback module:

java 代码
 
  1. -module(code_lock).  
  2. -behaviour(gen_fsm).  
  3.   
  4. -export([start_link/1]).  
  5. -export([button/1]).  
  6. -export([init/1, locked/2, open/2]).  
  7.   
  8. start_link(Code) ->  
  9.     gen_fsm:start_link({local, code_lock}, code_lock, Code, []).  
  10.   
  11. button(Digit) ->  
  12.     gen_fsm:send_event(code_lock, {button, Digit}).  
  13.   
  14. init(Code) ->  
  15.     {ok, locked, {[], Code}}.  
  16.   
  17. locked({button, Digit}, {SoFar, Code}) ->  
  18.     case [Digit|SoFar] of  
  19.         Code ->  
  20.             do_unlock(),  
  21.             {next_state, open, {[], Code}, 3000};  
  22.         Incomplete when length(Incomplete)  
  23.             {next_state, locked, {Incomplete, Code}};  
  24.         _Wrong ->  
  25.             {next_state, locked, {[], Code}};  
  26.     end.  
  27.   
  28. open(timeout, State) ->  
  29.     do_lock(),  
  30.     {next_state, locked, State}.  

The code is explained in the next sections.

3.3 Starting a Gen_Fsm

In the example in the previous section, the gen_fsm is started by calling code_lock:start_link(Code):

start_link(Code) ->
gen_fsm:start_link({local, code_lock}, code_lock, Code, []).

start_link calls the function gen_fsm:start_link/4. This function spawns and links to a new process, a gen_fsm.

  • The first argument {local, code_lock} specifies the name. In this case, the gen_fsm will be locally registered as code_lock.
    If the name is omitted, the gen_fsm is not registered. Instead its pid must be used. The name could also be given as {global, Name}, in which case the gen_fsm is registered using global:register_name/2.
  • The second argument, code_lock, is the name of the callback module, that is the module where the callback functions are located.
    In this case, the interface functions (start_link and button) are located in the same module as the callback functions (init, locked and open). This is normally good programming practice, to have the code corresponding to one process contained in one module.
  • The third argument, Code, is a term which is passed as-is to the callback function init. Here, init gets the correct code for the lock as indata.
  • The fourth argument, [], is a list of options. See gen_fsm(3) for available options.

If name registration succeeds, the new gen_fsm process calls the callback function code_lock:init(Code). This function is expected to return {ok, StateName, StateData}, where StateName is the name of the initial state of the gen_fsm. In this case locked, assuming the door is locked to begin with. StateData is the internal state of the gen_fsm. (For gen_fsms, the internal state is often referred to 'state data' to distinguish it from the state as in states of a state machine.) In this case, the state data is the button sequence so far (empty to begin with) and the correct code of the lock.

java 代码
  1. init(Code) ->  
  2.     {ok, locked, {[], Code}}.  
  3.       

Note that gen_fsm:start_link is synchronous. It does not return until the gen_fsm has been initialized and is ready to receive notifications.

gen_fsm:start_link must be used if the gen_fsm is part of a supervision tree, i.e. is started by a supervisor. There is another function gen_fsm:start to start a stand-alone gen_fsm, i.e. a gen_fsm which is not part of a supervision tree. 

3.4 Notifying About Events

The function notifying the code lock about a button event is implemented using gen_fsm:send_event/2:

java 代码
  1. button(Digit) ->  
  2.     gen_fsm:send_event(code_lock, {button, Digit}).  


code_lock is the name of the gen_fsm and must agree with the name used to start it. {button, Digit} is the actual event.

The event is made into a message and sent to the gen_fsm. When the event is received, the gen_fsm calls StateName(Event, StateData) which is expected to return a tuple {next_state, StateName1, StateData1}. StateName is the name of the current state and StateName1 is the name of the next state to go to. StateData1 is a new value for the state data of the gen_fsm.

java 代码
 
  1. locked({button, Digit}, {SoFar, Code}) ->  
  2.     case [Digit|SoFar] of  
  3.         Code ->  
  4.             do_unlock(),  
  5.             {next_state, open, {[], Code}, 30000};  
  6.         Incomplete when length(Incomplete)  
  7.             {next_state, locked, {Incomplete, Code}};  
  8.         _Wrong ->  
  9.             {next_state, locked, {[], Code}};  
  10.     end.  
  11.   
  12. open(timeout, State) ->  
  13.     do_lock(),  
  14.     {next_state, locked, State}.  

If the door is locked and a button is pressed, the complete button sequence so far is compared with the correct code for the lock and, depending on the result, the door is either unlocked and the gen_fsm goes to state open, or the door remains in state locked.

3.5 Timeouts

When a correct code has been givened, the door is unlocked and the following tuple is returned from locked/2:

java 代码
 
  1. {next_state, open, {[], Code}, 30000};  

30000 is a timeout value in milliseconds. After 30000 ms, i.e. 30 seconds, a timeout occurs. Then StateName(timeout, StateData) is called. In this case, the timeout occurs when the door has been in state open for 30 seconds. After that the door is locked again:

java 代码
  1. open(timeout, State) ->  
  2.     do_lock(),  
  3.     {next_state, locked, State}.  


3.6 All State Events

Sometimes an event can arrive at any state of the gen_fsm. Instead of sending the message with gen_fsm:send_event/2 and writing one clause handling the event for each state function, the message can be sent with gen_fsm:send_all_state_event/2 and handled with Module:handle_event/3:

java 代码
 
  1. -module(code_lock).  
  2. ...  
  3. -export([stop/0]).  
  4. ...  
  5.   
  6. stop() ->  
  7.     gen_fsm:send_all_state_event(code_lock, stop).  
  8.   
  9. ...  
  10.   
  11. handle_event(stop, _StateName, StateData) ->  
  12.     {stop, normal, StateData}.  



3.7 Stopping


3.7.1 In a Supervision Tree

If the gen_fsm is part of a supervision tree, no stop function is needed. The gen_fsm will automatically be terminated by its supervisor. Exactly how this is done is defined by a shutdown strategy set in the supervisor.

If it is necessary to clean up before termination, the shutdown strategy must be a timeout value and the gen_fsm must be set to trap exit signals in the init function. When ordered to shutdown, the gen_fsm will then call the callback function terminate(shutdown, StateName, StateData):

java 代码
 
  1. init(Args) ->  
  2.     ...,  
  3.     process_flag(trap_exit, true),  
  4.     ...,  
  5.     {ok, StateName, StateData}.  
  6.   
  7. ...  
  8.   
  9. terminate(shutdown, StateName, StateData) ->  
  10.     ..code for cleaning up here..  
  11.     ok.  


3.7.2 Stand-Alone Gen_Fsms

If the gen_fsm is not part of a supervision tree, a stop function may be useful, for example:

java 代码
 
  1. ...  
  2. -export([stop/0]).  
  3. ...  
  4.   
  5. stop() ->  
  6.     gen_fsm:send_all_state_event(code_lock, stop).  
  7. ...  
  8.   
  9. handle_event(stop, _StateName, StateData) ->  
  10.     {stop, normal, StateData}.  
  11.   
  12. ...  


terminate(normal, _StateName, _StateData) ->
ok.

The callback function handling the stop event returns a tuple {stop,normal,StateData1}, where normal specifies that it is a normal termination and StateData1 is a new value for the state data of the gen_fsm. This will cause the gen_fsm to call terminate(normal,StateName,StateData1) and then terminate gracefully:

3.8 Handling Other Messages

If the gen_fsm should be able to receive other messages than events, the callback function handle_info(Info, StateName, StateData) must be implemented to handle them. Examples of other messages are exit messages, if the gen_fsm is linked to other processes (than the supervisor) and trapping exit signals.

java 代码
  1. handle_info({'EXIT', Pid, Reason}, StateName, StateData) ->  
  2.     ..code to handle exits here..  
  3.     {next_state, StateName1, StateData1}.  

分享到:
评论
2 楼 wenew 2008-03-01  
这是erlang mailist里的一个可正常编译的例子,
但我还是不大明白,
如我:
{ok,code_lock}
2> code_lock:start(332).
{ok,<0.38.0>}
3> code_lock:button(332).
ok
4>
如果button了正确的代码,应该会有nI am an unlocked lock才对呀!
1 楼 wenew 2008-03-01  
-module(code_lock).
-behaviour(gen_fsm).

-export([init/1, button/1, locked/2, open/2]).

-export([start/1, handle_event/3, handle_sync_event/4, handle_info/3,
         terminate/3, code_change/4]).

handle_event(_Event, StateName, State) ->
    {next_state, StateName, State}.

handle_sync_event(_Event, _From, StateName, State) ->
    Reply = ok,
    {reply, Reply, StateName, State}.

handle_info(_Info, StateName, State) ->
    {next_state, StateName, State}.

terminate(_Reason, _StateName, _State) ->
    ok.

code_change(_OldVsn, StateName, State, _Extra) ->
    {ok, StateName, State}.

start(Code) ->
    gen_fsm:start({local, code_lock}, code_lock, Code, []).

button(Digit) ->
    gen_fsm:send_event(code_lock, {button, Digit}).

init(Code) ->
    {ok, locked, {[], Code}}.

locked({button, 9}, {SoFar, Code}) ->
    9/0,
    {next_state, locked, {SoFar, 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}.

do_lock() ->
    io:format("~nI am a locked lock.~n").

do_unlock() ->
    io:format("~nI am an unlocked lock.~n").

相关推荐

    erlang OTP Design Principles之Gen中文

    Erlang OTP设计原则中的Gen_Fsm行为是一个关键的概念,用于构建健壮、可扩展的并发应用程序。Gen_Fsm,即通用有限状态机,是一种行为模式,它提供了一种结构化的方法来处理具有多种状态和事件的系统。本文将深入探讨...

    Erlang OTP设计原理文档 中文版本

    OTP设计原则着重于实现高度并发、容错性和高效能。下面将详细讨论Erlang OTP中的关键概念和相关知识点。 1. 监督树(Supervision Tree): 监督树是OTP设计的核心概念,它是一种组织Erlang进程的方式。每个节点都...

    使用OTP原理构建一个非阻塞的TCP服务器

    OTP为开发健壮、容错的应用提供了强大的框架,它包含了一系列行为模式,如gen_server和gen_fsm,这些模式可以帮助我们构建高效、可扩展的服务。 首先,我们需要理解什么是非阻塞TCP服务器。在Erlang中,"非阻塞"指...

    otp_src_22.0_h.tar.gz

    OTP提供了几个设计良好的应用框架,如gen_server、gen_event、gen_fsm等,它们是基于行为模式的模块,简化了并发编程和状态管理。 5. **分布式功能** OTP支持跨节点的分布式计算,使得在多台机器上构建分布式系统...

    Erlang otp_win64_21.1.exe otp_win32_21.1.exe

    它包含了一系列预定义的模块、行为(如GenServer、GenEvent和Gen_fsm)以及设计原则,这些都旨在帮助开发者创建可靠和可扩展的应用程序。例如,Erlang的进程模型允许程序中的组件独立运行,通过消息传递进行通信,这...

    基于Erlang的gen_tcp聊天室代码,功能完整

    gen_tcp是Erlang OTP(开放电信平台)提供的一种行为模块,它允许程序员以面向过程的方式处理TCP连接。gen_tcp提供了创建、监听、接受和关闭TCP套接字的函数,以及发送和接收数据的基本操作。 **Erlang的并发特性**...

    OTP Design Principles

    ### OTP设计原则详解 #### 概览 OTP(Open Telecom Platform)是Erlang/OTP框架的核心组成部分之一,它提供了一套成熟的、可扩展的、容错的应用程序设计模式。OTP设计原则指导开发者如何构建稳定可靠的分布式系统...

    Erlang_OTP_设计原理(含目录).pdf

    Erlang OTP设计原理是一份深入探讨Erlang/OTP(Open Telecom Platform)框架中设计模式和组织代码原则的文档。Erlang OTP作为Erlang语言的中间件平台,提供了构建可扩展和容错系统的标准方法。 文档开篇就介绍了...

    otp_win64_20.3.rar

    2. **行为模式**: 如GenServer、GenEvent和Gen_fsm等,提供了一种结构化的方式来实现常见的并发设计模式。 3. **应用和发布**: OTP应用程序结构定义了如何组织源代码,以及如何管理和部署应用程序。 ** Erlang ** ...

    otp_win64_23.2.exe

    5. **行为模式**:OTP定义了几种预定义的行为模式,如GenServer、GenEvent和Gen_fsm,这些模式提供了一种组织和管理Erlang进程的标准方式,帮助实现常见的并发和状态管理问题。 6. **公共接口**:OTP还包含各种标准...

    otp_src_21.1.tar.gz

    1. **模块化设计**:OTP提供了各种预定义的进程行为模式(gen_server、gen_event、gen_fsm等),便于开发者创建符合特定模式的进程,提高了代码复用和可维护性。 2. **分布式计算**:Erlang OTP支持跨节点的进程...

    erlang的翻译文档

    文档主要分为两大部分:入门指南和OTP设计原则。 ##### 1. 入门指南 - **简介**:简要介绍了Erlang的基本概念,旨在引导新手快速入门。 - **介绍**:概述Erlang的基本特性和应用场景。 - **其他方面**:列出了...

    erlang_otp_src_17.3.tar.gz

    7. **行为模块**:如gen_server、gen_event、gen_fsm等,是OTP设计模式的具体实现,简化了编写服务器、事件处理器和有限状态机的代码。 关于压缩包内的"otp_src_17.3",这是Erlang OTP 17.3版本的源代码目录。为了...

    otp_src_R11B-5.tar.gz_OTP_erlang_otp-src-R11B_otp_s

    这些库包括Mnesia(分布式数据库)、Event Logger、公共接口定义语言(CIDL)以及行为模式如GenServer、GenEvent和Gen_fsm等。这些行为模式为开发者提供了构建状态管理、事件处理和分布式服务的标准结构,使得代码...

    Erlang otp_win64_22.0

    10. **行为模块**:如GenServer、GenEvent和Gen_fsm等,是OTP的一部分,它们提供了一种组织代码和处理并发行为的标准方式。 压缩包子文件"otp_win64_22.0.exe"的安装流程通常包括以下步骤: 1. 下载并运行安装程序...

    otp_src_19.1.tar.gz

    4. **行为模块**:OTP库包含了一系列预定义的行为模块,如GenServer、GenEvent和Gen_fsm等,它们提供了标准的接口和状态管理,简化了复杂应用的开发。 5. **错误恢复和容错机制**:Erlang OTP设计了一套强大的错误...

    Erlang otp_win64_21和22版本 exe

    - **行为模块**:如GenServer、GenEvent和Gen_fsm等,它们定义了常见的并发模式,简化了状态管理和事件处理。 - **应用程序管理**:OTP提供了应用程序框架,用于组织和管理软件组件,确保其按预期启动、停止和升级...

    otp_win64_25.0.4.rar

    7. **行为模式**:如GenServer、GenEvent和Gen_fsm等,为常见的设计模式提供抽象,简化了状态管理和事件处理。 8. **热升级**:Erlang系统支持在线代码升级,无需停止服务即可更新应用程序。 9. **编译器与虚拟机*...

    Erlang中文手册.pdf

    ### OTP设计原则 #### 2.1 概述 - **2.1.1 监督树**:介绍了Erlang OTP框架中的监督树概念。 - **2.1.2 Behaviour**:行为模块是用于编写特定类型进程的模板。 - **2.1.3 应用**:解释如何将模块和行为组合成应用...

Global site tag (gtag.js) - Google Analytics