在处理状态超时时优雅地退出 gen_statem

Posted

技术标签:

【中文标题】在处理状态超时时优雅地退出 gen_statem【英文标题】:Gracefully exit from a gen_statem while processing a state timeout 【发布时间】:2019-01-15 21:01:43 【问题描述】:

如何优雅地退出 gen_statem 中的 state_timeout?通常,对于 gen_statem,我可以从状态函数返回“stop”或 stop, Reason,然后 statem 将终止。但是,如果我使用状态超时方法,从 init 返回:

ok, StateName, State, state_timeout, 2000, timeout

handle_event(state_timeout, timeout, StateName, State) 方法在 2000 年之后被调用。从这里返回“stop”会报错……:

代码:

handle_event(state_timeout, timeout, StateName, State) -> lager:warning("设备超时: ~p in State: ~p", [State#state.uuid, StateName]), 停止,超时;

错误:

14:29:56.992 [警告] handle_event(299): 超时 设备:未定义状态:init_auth 14:29:56.992 [错误] 未定义(未定义):Lager 事件处理程序 error_logger_lager_h 已退出 原因是 'EXIT',badmatch,[module,state_timeout,timeout

我可以从这个函数返回什么来优雅地退出?这可以用 state_timeout 完成吗?如果这是一个明显的问题,请指出我正确的方向。这里的文档:http://erlang.org/doc/man/gen_statem.html#type-transition_option 似乎没有提到 state_timeout 的任何返回。似乎我可以指定“下一个状态”,但似乎没有定义退出

感谢您的帮助!

【问题讨论】:

【参考方案1】:

系统生成错误,因为与停止消息相关的原因不是normal

在这个小例子中,状态机在达到 20 个超时条件后“优雅地”退出:

-module (statem).

-behaviour(gen_statem).

-export ([start_link/0]).
-export([init/1, callback_mode/0, terminate/3, code_change/4,handle_event/4]).

start_link() ->
    gen_statem:start_link(local,?MODULE, ?MODULE, [], []).

init([]) ->
    ok, state1, #info => 0, call => 0 , cast => 0, timeout => 0,[timeout, 2000, timeout_event].


callback_mode() ->
    handle_event_function.

handle_event(timeout,_EventContent,_State,#timeout := 20) ->
    io:format("max timeout count reached~n"),
    stop, normal;
handle_event(EventType,EventContent,State,Data) ->
    io:format("EtventType : ~p~nEventContent : ~p~nState : ~p~nData : ~p~n", [EventType,EventContent,State,Data]),
    NewData = process_event(EventType,EventContent,State,Data),
    next_state, State, NewData,[timeout, 2000, timeout_event].

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

code_change(_Vsn, State, Data, _Extra) ->
    ok, State, Data.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

process_event(info,_EventContent,_State,Data) ->
    maps:update(info, maps:get(info, Data)+1, Data);
process_event(cast,_EventContent,_State,Data) ->
    maps:update(cast, maps:get(cast, Data)+1, Data);
process_event(timeout,_EventContent,_State,Data) ->
    maps:update(timeout, maps:get(timeout, Data)+1, Data);
process_event(call,From,EventContent,_State,Data) ->
    gen_statem:reply(From, got,EventContent),
    maps:update(call, maps:get(call, Data)+1, Data).

工作中:

74> c(statem).                     
ok,statem
75> ok,Pid = statem:start_link().
ok,<0.197.0>
EtventType : timeout               
EventContent : timeout_event
State : state1
Data : #call => 0,cast => 0,info => 0,timeout => 0
EtventType : timeout              
EventContent : timeout_event
State : state1
Data : #call => 0,cast => 0,info => 0,timeout => 1
76> Pid ! hello.                  
EtventType : info
EventContent : hello
State : state1
Data : #call => 0,cast => 0,info => 0,timeout => 2
hello
EtventType : timeout               
EventContent : timeout_event
State : state1
Data : #call => 0,cast => 0,info => 1,timeout => 2
EtventType : timeout              
EventContent : timeout_event
State : state1
Data : #call => 0,cast => 0,info => 1,timeout => 3
77> gen_statem:cast(Pid,cast_msg).
EtventType : cast
EventContent : cast_msg
State : state1
Data : #call => 0,cast => 0,info => 1,timeout => 4
ok
...
80> gen_statem:call(Pid,call_msg).
EtventType : call,<0.190.0>,#Ref<0.571135265.1570766849.45359>
EventContent : call_msg
State : state1
Data : #call => 0,cast => 3,info => 1,timeout => 17
got,call_msg
EtventType : timeout
EventContent : timeout_event
State : state1
Data : #call => 1,cast => 3,info => 1,timeout => 17
...
EtventType : timeout              
EventContent : timeout_event
State : state1
Data : #call => 2,cast => 4,info => 1,timeout => 19
max timeout count reached
84> 

编辑]

它与语法 state_timeout 相同(我没有深入了解差异)

您的 sn-p 中存在语法错误,操作必须包含在列表中(即使只有一个操作)。我尝试了这个修改后的代码,它可以工作:

-module (statem).

-behaviour(gen_statem).

-export ([start_link/0]).
-export([init/1, callback_mode/0, terminate/3, code_change/4,handle_event/4]).

start_link() ->
    gen_statem:start_link(local,?MODULE, ?MODULE, [], []).

init([]) ->
    ok, state1, #info => 0, call => 0 , cast => 0, timeout => 0,[state_timeout, 2000, timeout].


callback_mode() ->
    handle_event_function.

handle_event(state_timeout,_EventContent,_State,#timeout := 20) ->
    io:format("max timeout count reached~n"),
    stop, normal;
handle_event(EventType,EventContent,State,Data) ->
    io:format("EtventType : ~p~nEventContent : ~p~nState : ~p~nData : ~p~n", [EventType,EventContent,State,Data]),
    NewData = process_event(EventType,EventContent,State,Data),
    next_state, State, NewData,[state_timeout, 2000, timeout].

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

code_change(_Vsn, State, Data, _Extra) ->
    ok, State, Data.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

process_event(info,_EventContent,_State,Data) ->
    maps:update(info, maps:get(info, Data)+1, Data);
process_event(cast,_EventContent,_State,Data) ->
    maps:update(cast, maps:get(cast, Data)+1, Data);
process_event(state_timeout,_EventContent,_State,Data) ->
    maps:update(timeout, maps:get(timeout, Data)+1, Data);
process_event(call,From,EventContent,_State,Data) ->
    gen_statem:reply(From, got,EventContent),
    maps:update(call, maps:get(call, Data)+1, Data).

【讨论】:

这是使用超时方法,而不是state_timeout。你知道为什么 state_timeout 方法不起作用吗?

以上是关于在处理状态超时时优雅地退出 gen_statem的主要内容,如果未能解决你的问题,请参考以下文章

Vuetify 小吃吧超时时更新父状态

如何优雅地使用Sublime Text

AWS S3 在 getSignedUrl 过期后优雅地处理 403

java sqlserver 怎么做当某一笔数据超时时,自动更新该笔数据状态?

95-22-010-停止-优雅停机

当它已经超时时,我们如何避免多个 Rebus 消息?