Erlang:在不干扰超时的情况下观察gen_服务器状态

Erlang:在不干扰超时的情况下观察gen_服务器状态,erlang,Erlang,我有一个模拟gen_fsm的gen_服务器,(不要问我为什么…),一个gen_服务器:调用将导致此gen_服务器从当前状态转移到下一个状态,如果在某个时间没有调用gen_服务器:调用,gen_服务器将终止 更具体地说,gen_服务器的寿命如下所示: handle_call(get_a, _From, #state{a = 1, state = state_2, %

我有一个模拟gen_fsm的gen_服务器,(不要问我为什么…),一个
gen_服务器:调用将导致此gen_服务器从当前状态转移到下一个状态,如果在某个时间没有调用
gen_服务器:调用
,gen_服务器将终止

更具体地说,gen_服务器的寿命如下所示:

handle_call(get_a, _From, #state{a = 1,
                                 state = state_2,

                                 %% this is the timestamp when the server transfered to state_2
                                 unixtime = T1

                                }=S) ->
    Reply = S#state.a,
    NewTimeout = t_2 - (current_unixtime() - T1),
    {reply, Reply, S, NewTimeout};
state\u 1->state\u 2->…->状态->终止

当服务器处于状态_i时,如果此服务器上没有调用gen_server调用,则在t_i秒后,服务器将进入终止状态,当然,这是通过使用
{reply,reply,NewState,t_i}
作为
句柄_调用/3
的返回来实现的

这个方法的问题是,我无法从这个gen_服务器检索一些信息,因为要这样做,我需要调用gen_服务器:调用它,这会弄乱超时

一种可能的解决方法是将最后一个状态转移时间戳放入状态,在每次检索调用后,将新超时重置为适当的值,原型如下所示:

handle_call(get_a, _From, #state{a = 1,
                                 state = state_2,

                                 %% this is the timestamp when the server transfered to state_2
                                 unixtime = T1

                                }=S) ->
    Reply = S#state.a,
    NewTimeout = t_2 - (current_unixtime() - T1),
    {reply, Reply, S, NewTimeout};

通过这种方式,我可以获得我想要的效果,但这很难看,有没有更好的方法来实现这一点?

如果您想独立于其他事件(如呼叫)设置超时,可能最好使用定时消息

设置超时时,请使用erlang:send\u after/3:

TimerRef = erlang:send_after(10000, self(), my_timeout).
您可以使用erlang:cancel\u timer/1随时取消超时

erlang:cancel_timer(TimerRef).
使用句柄信息接收它:

 handle_info(my_timeout, State) ->
如果需要多个此类超时,请使用不同的消息,或者如果可能存在某种竞争条件并需要进一步控制,则可以使用erlang创建唯一引用:make_ref/0并发送类似{ref,my_timeout}的消息

注意边缘情况-请记住,您可能会取消计时器,但仍然意外地收到它(因为取消计时器时它在您的消息队列中),并且您不会将它们设置为唯一的(如上所述,使用引用),您可能会期待一个超时,并提前获得它,因为它是进入您的消息队列的前一个计时器,etc(如有参考,您可以检查它是最新的一套)。这些事情很容易处理,但要小心