Erlang:观察 gen_server 状态而不干扰超时

标签 erlang

我有一个模仿 gen_fsm 的 gen_server,(不要问我为什么......),gen_server:call 会导致这个 gen_server 从当前状态转移到下一个状态,如果在一定时间内没有调用 gen_server:call,gen_server 将终止。

更具体地说,gen_server 的生命周期如下所示:

state_1 -> state_2 -> ... -> state_N -> 终止

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

该方法的问题是,我无法从该 gen_server 检索一些信息,为此,我需要调用 gen_server:call ,这会扰乱超时。

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

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_after/3:

TimerRef = erlang:send_after(10000, self(), my_timeout).

您可以随时使用 erlang:cancel_timer/1 取消超时。

erlang:cancel_timer(TimerRef).

通过handle_info接收:

 handle_info(my_timeout, State) ->

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

注意边缘情况 - 请记住,您可能会取消计时器,然后仍然意外收到它(因为取消时它在您的消息队列中),并且您不会使它们唯一(如上面使用引用建议的那样)您可能会期待超时,并尽早获取它,因为它是进入消息队列的上一个消息,等等(与引用一样,您可以检查它是否是最新的一组)。这些事情很容易处理,但要小心。

关于Erlang:观察 gen_server 状态而不干扰超时,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33341098/

相关文章:

multithreading - "How many links do I have?",询问 Erlang 进程

Erlang 代码将不带分隔符的字符串拆分并放入列表

ios - Erlang gen_tcp 关闭被 GCDAsynchSocket 弄错的端口

reference - ETS创建返回值

erlang - 如何修改erlang中的记录?

erlang - 为什么数学在 Erlang/BEAM VM 中这么慢?

ubuntu - Rabbitmq 监听所有接口(interface)

performance - 以下两段代码哪一段更快或相同,为什么?

concurrency - 如何使已编写的并发程序在GPU阵列上运行?

Erlang C节点相关问题