Erlang -- gen_fsm

gen_fsm

gen_fsm为有限状态机,在OTP 20.0被gen_statem取代。
gen_fsmbehavior函数和回调函数之间的关系如下:

gen_fsm module                    Callback module
--------------                    ---------------
gen_fsm:start
gen_fsm:start_link                -----> Module:init/1

gen_fsm:stop                      -----> Module:terminate/3

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

方法gen_fsm:send_event(FsmRef, Event)会给状态机进程异步发送一个事件Event,状态机会调用回调函数Module:StateName(Event, State)来处理该事件,其中StateName为状态机当前的状态。

方法gen_fsm:end_all_state_event(FsmRef, Event)会给状态机进程异步发送一个事件Event,状态机会调用回调函数Module:handle_event(Event, StateName, StateData)来处理该事件,其中StateName为状态机当前的状态。
二者区别在于回调函数不同。后者可用于在所有状态下对事件的处理都是一样的情况下,这样可以不用为每个状态去写一个分支。

方法gen_fsm:sync_send_event(FsmRef, Event)gen_fsm:sync_send_event(FsmRef, Event, TimeOut)会给状态机进程同步发送一个事件Event并等待状态机返回结果,如果TimeOut ms(默认5000ms)内未返回则调用失败。状态机会调用回调函数Module:StateName(Event, From, State)来处理该事件,并将结果返回给From进程,其中StateName为状态机当前的状态。

对于通过gen_fsm:sync_send_all_state_event(FsmRef, Event)gen_fsm:sync_send_all_state_event(FsmRef, Event, Timeout)发送的事件,状态机通过回调函数Module:handle_sync_event(Event, From, StateName, StateData)返回。

方法gen_fsm:send_event_after(Time, Event)会在Time ms后给状态机进程异步发送一个事件Event,并返回一个可用cancel_timer(Ref)取消的定时器引用Ref。状态机会调用回调函数Module:StateName(Event, State)来处理该事件,其中StateName为发送事件时状态机的状态。

方法gen_fsm:start_timer(Time, Msg)会在Time ms后给状态机进程异步发送一个事件Event,并返回一个可用cancel_timer(Ref)取消的定时器引用Ref。状态机会调用回调函数Module:StateName({timeout, Ref, Msg}, State)来处理该事件,其中StateName为发送事件时状态机的状态。

例子
-module(code_lock). 
-define(NAME, code_lock).
-behaviour(gen_fsm). 

-export([start_link/1, button/1, stop/0]). 

-export([
	init/1, 
	locked/2, 
	open/2, 
	handle_sync_event/4, 
	handle_event/3, 
	handle_info/3, 
	terminate/3, 
	code_change/4]). 

start_link(Code) -> 
	gen_fsm:start_link({local, ?NAME}, ?MODULE, Code, []). 

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

stop() -> 
	gen_fsm:sync_send_all_state_event(?NAME, stop). 

init(Code) -> 
	do_lock(), Data = #{code => Code, remaining => Code}, {ok, locked, Data}. 

locked({button, Digit}, Data0) -> 
	case analyze_lock(Digit, Data0) of 
		{open = StateName, Data} -> 
			{next_state, StateName, Data, 10000}; 
		{StateName, Data} -> 
			{next_state, StateName, Data} 
	end. 

open(timeout, State) -> 
	do_lock(), 
	{next_state, locked, State}; 
open({button,_}, Data) -> 
	{next_state, locked, Data}. 

handle_sync_event(stop, _From, _StateName, Data) -> 
	{stop, normal, ok, Data}. 
handle_event(Event, StateName, Data) -> 
	{stop, {shutdown, {unexpected, Event, StateName}}, Data}. 
handle_info(Info, StateName, Data) -> 
	{stop, {shutdown, {unexpected, Info, StateName}}, StateName, Data}. 

terminate(_Reason, State, _Data) -> 
	State =/= locked andalso do_lock(),
	ok. 
code_change(_Vsn, State, Data, _Extra) -> 
	{ok, State, Data}.

analyze_lock(Digit, #{code := Code, remaining := Remaining} = Data) -> 
	case Remaining of 
		[Digit] -> 
			do_unlock(), 
			{open, Data#{remaining := Code}}; 
		[Digit|Rest] -> 
		% Incomplete 
			{locked, Data#{remaining := Rest}}; 
		_Wrong -> 
			{locked, Data#{remaining := Code}} 
	end. 
do_lock() -> 
	io:format("Lock~n", []). 
do_unlock() -> 
	io:format("Unlock~n", []).
把gen_fsm改写成gen_statem
%% https://erldocs.com/20.0/stdlib/gen_fsm.html#Migration%20to%20gen_statem
-module(code_lock). 
-define(NAME, code_lock). 
%-define(BEFORE_REWRITE, true). 
-ifdef(BEFORE_REWRITE). 
-behaviour(gen_fsm). 
-else. 
-behaviour(gen_statem). 
-endif. 

-export([start_link/1, button/1, stop/0]). 

-ifdef(BEFORE_REWRITE). 
-export([
	init/1, 
	locked/2, 
	open/2, 
	handle_sync_event/4, 
	handle_event/3, 
	handle_info/3, 
	terminate/3, 
	code_change/4]). 
-else. 
-export([
	init/1
	, callback_mode/0
	, locked/3
	, open/3
	, terminate/3
	, code_change/4]). 
%% Add callback__mode/0 
%% Change arity of the state functions 
%% Remove handle_info/3 
-endif. 

-ifdef(BEFORE_REWRITE). 
start_link(Code) -> 
	gen_fsm:start_link({local, ?NAME}, ?MODULE, Code, []). 
-else. 
start_link(Code) -> 
	gen_statem:start_link({local,?NAME}, ?MODULE, Code, []). 
-endif. 

-ifdef(BEFORE_REWRITE). 
button(Digit) -> 
	gen_fsm:send_event(?NAME, {button, Digit}). 
-else. 
button(Digit) -> 
	gen_statem:cast(?NAME, {button,Digit}). 
%% send_event is asynchronous and becomes a cast 
-endif. 

-ifdef(BEFORE_REWRITE). 
stop() -> 
	gen_fsm:sync_send_all_state_event(?NAME, stop). 
-else. 
stop() -> 
	gen_statem:call(?NAME, stop). 
%% sync_send is synchronous and becomes call 
%% all_state is handled by callback code in gen_statem 
-endif. 

init(Code) -> 
	do_lock(), Data = #{code => Code, remaining => Code}, {ok, locked, Data}. 

-ifdef(BEFORE_REWRITE). 
-else. 
callback_mode() -> state_functions. 
%% state_functions mode is the mode most similar to 
%% gen_fsm. There is also handle_event mode which is 
%% a fairly different concept. 
-endif.

-ifdef(BEFORE_REWRITE). 
locked({button, Digit}, Data0) -> 
	case analyze_lock(Digit, Data0) of 
		{open = StateName, Data} -> 
			{next_state, StateName, Data, 10000}; 
		{StateName, Data} -> 
			{next_state, StateName, Data} 
	end. 
-else. 
locked(cast, {button,Digit}, Data0) -> 
	case analyze_lock(Digit, Data0) of 
		{open = StateName, Data} -> 
			{next_state, StateName, Data, 10000}; 
		{StateName, Data} -> 
			{next_state, StateName, Data} 
	end; 
locked({call, From}, Msg, Data) -> 
	handle_call(From, Msg, Data); 
locked({info, Msg}, StateName, Data) -> 
	handle_info(Msg, StateName, Data). 
%% Arity differs 
%% All state events are dispatched to handle_call and handle_info help 
%% functions. If you want to handle a call or cast event specifically 
%% for this state you would add a special clause for it above. 
-endif. 

-ifdef(BEFORE_REWRITE). 
open(timeout, State) -> 
	do_lock(), 
	{next_state, locked, State}; 
open({button,_}, Data) -> 
	{next_state, locked, Data}. 
-else. 
open(timeout, _, Data) -> 
	do_lock(), 
	{next_state, locked, Data}; 
open(cast, {button,_}, Data) -> 
	{next_state, locked, Data}; 
open({call, From}, Msg, Data) -> 
	handle_call(From, Msg, Data); 
open(info, Msg, Data) -> 
	handle_info(Msg, open, Data). 
%% Arity differs 
%% All state events are dispatched to handle_call and handle_info help 
%% functions. If you want to handle a call or cast event specifically 
%% for this state you would add a special clause for it above. 
-endif. 

-ifdef(BEFORE_REWRITE). 
handle_sync_event(stop, _From, _StateName, Data) -> 
	{stop, normal, ok, Data}. 
handle_event(Event, StateName, Data) -> 
	{stop, {shutdown, {unexpected, Event, StateName}}, Data}. 
handle_info(Info, StateName, Data) -> 
	{stop, {shutdown, {unexpected, Info, StateName}}, StateName, Data}. 
-else. 
-endif.

terminate(_Reason, State, _Data) -> 
	State =/= locked andalso do_lock(),
	ok. 
code_change(_Vsn, State, Data, _Extra) -> 
	{ok, State, Data}.

%% Internal functions 
-ifdef(BEFORE_REWRITE). 
-else. 
handle_call(From, stop, Data) -> 
	{stop_and_reply, normal, {reply, From, ok}, Data}. 
handle_info(Info, StateName, Data) -> 
	{stop, {shutdown, {unexpected, Info, StateName}}, StateName, Data}. 
%% These are internal functions for handling all state events 
%% and not behaviour callbacks as in gen_fsm 
-endif.

analyze_lock(Digit, #{code := Code, remaining := Remaining} = Data) -> 
	case Remaining of 
		[Digit] -> 
			do_unlock(), 
			{open, Data#{remaining := Code}}; 
		[Digit|Rest] -> 
		% Incomplete 
			{locked, Data#{remaining := Rest}}; 
		_Wrong -> 
			{locked, Data#{remaining := Code}} 
	end. 
do_lock() -> 
	io:format("Lock~n", []). 
do_unlock() -> 
	io:format("Unlock~n", []).
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值