是否有一种 Erlang 行为可以自行行动而不是等待被调用?

Is there an Erlang behaviour that can act on its own instead of waiting to be called?

我正在编写一个需要主动轮询一些远程资源的 Erlang 应用程序,我希望进行轮询的进程适合 OTP 监督树并支持所有标准设施,如正确终止、热代码重新加载、等等

然而,gen_servergen_fsm这两个默认行为似乎只支持基于回调的操作。我可以滥用 gen_server 通过调用自我或滥用 gen_fsm 来实现这一点,方法是让一个状态始终以超时 0 循环到自身,但我不确定这是否安全(即不安全耗尽堆栈或在邮箱中累积未读邮件)。

我可以将我的流程变成 special process 并自己编写所有处理程序,但这实际上让我重新实现了 Erlang 等价的轮子。

这样的代码有行为吗?

loop(State) ->
  do_stuff(State), % without waiting to be called
  loop(NewState).

如果没有,是否有一种安全的方法来欺骗默认行为来执行此操作,而不会耗尽堆栈或随着时间的推移积累消息或其他什么?

在 Erlang 中执行此操作的标准方法是使用 erlang:send_after/3。看到这个 SO answer and also this example implementation.

您是否可以采用本质上不符合 OTP 的流程?尽管要成为一名优秀的 OTP 公民,您确实希望将漫长的 运行ning 过程变成 gen_server 和 gen_fsm,但有时您必须超越标准问题规则预订并考虑规则存在的原因。

例如,如果您的主管启动了您的 gen_server,并且您的 gen_server 产生了另一个进程(我们称之为 active_poll 进程),他们 link 彼此分享命运(如果一个人死了另一个人死了)。 active_poll 进程现在由生成 gen_server 的主管间接监督,因为如果它死了,gen_server 也会死,它们都会重新启动。您现在真正需要解决的唯一问题是代码升级,但这并不太难 - 当代码要升级时,您的 gen_server 会收到一个 code_change 回调调用,它可以简单地发送一条消息到 active_poll 进程,它可以进行适当的完全合格的函数调用,然后宾果游戏,它正在 运行 宁新代码。

如果由于某种原因这不适合您 and/or 您必须直接使用 gen_server/gen_fsm/similar...

我不确定写一个'special process'真的给你很大的帮助。如果你正确地编写了一个特殊的进程,使得它在理论上符合 OTP 设计原则,但如果它在某个地方阻塞或忙于循环等待并且在它不调用 sys 时它在实践中仍然可能无效应该,所以你真的最多比使用零超时的 gen_server/gen_fsm 有一个小的优化(或者通过一个异步消息处理程序来进行轮询并向自己发送消息以触发下一个轮询)。

如果你正在做的主动轮询可以阻塞(例如阻塞套接字读取),这真的是个大麻烦,因为gen_server,gen_fsm或一个特殊的进程会所有人都无法履行他们通常的义务(他们通常能够做到这一点,因为在 gen_server/gen_fsm returns 的情况下回调,或者因为 receive 被调用并且 sys 模块在特殊进程的情况下显式调用)。

如果你正在做的主动轮询是非阻塞的,你可以这样做,但如果你没有任何延迟地轮询,那么它实际上变成了一个繁忙的等待(这不完全是因为循环将在某处包含一个接收调用,这意味着该进程将屈服,从而为调度程序提供 运行 其他进程的自愿机会,但距离不远,它仍然是一个相对的 CPU 猪)。如果您可以在每次轮询之间有 1 毫秒的延迟,那么与尽可能快地进行轮询相比,情况会大不相同。这不是理想的,但如果你必须,它会工作。因此,请使用超时(尽可能大而不会成为问题),或者使用异步消息处理程序进行轮询并向自身发送消息以触发下一次轮询。