在this示例中,作者通过执行以下操作避免了死锁情况:
self() ! {start_worker_supervisor, Sup, MFA}
在他的gen_server的init函数中。我在我的一个项目中做了类似的事情,被告知这个方法不受欢迎,而且最好立即引起超时。什么是可接受的模式?
答案 0 :(得分:14)
考虑使用新的gen_statem
行为。此行为支持生成FSM内部的事件:
state函数可以使用action()next_event插入事件,并且这样的事件作为next存在插入到state函数中。也就是说,好像它是最老的传入事件。专门的event_type()内部可用于此类事件,使其无法误认为是外部事件。
插入事件取代了调用您自己的状态处理函数的技巧,例如,gen_fsm强制在其他函数之前处理插入的事件。
使用该模块中的action functionality,您可以确保在init
生成事件,并始终在任何外部事件之前处理,特别是在{{1}中创建next_event
操作功能。
示例:
init
在设计...
callback_mode() -> state_functions.
init(_Args) ->
{ok, my_state, #data{}, [{next_event, internal, do_the_thing}]}
my_state(internal, do_the_thing, Data) ->
the_thing(),
{keep_state, Data);
my_state({call, From}, Call, Data) ->
...
...
时,您通常可以选择在三种不同的状态下执行操作:
gen_server
init/1
函数handle_*
一个好的经验法则是在处理事件(调用,演员,消息等)时执行处理函数中的事情。在init中执行的东西不应该等待事件,这就是句柄回调的用途。
因此,在这种特殊情况下,会产生一种“假”事件。我会说terminate/2
似乎总是想要启动主管。为什么不直接在gen_server
中进行?是否真的需要能够处理中间的另一条消息(在init/1
中执行此操作的效果)?那个windown非常小(从handle_info/2
开始到将消息发送到gen_server
之间的时间)所以它根本不太可能发生。
至于死锁,我真的建议不要在你的init函数中调用你自己的主管。这只是不好的做法。启动工人流程的良好设计模式将是一个顶级主管,下面是经理和工人主管。经理通过致电工人主管来启动工人:
self()
答案 1 :(得分:7)
仅补充已经说过的关于将服务器初始化分为两部分的内容,第一部分在init/1
函数中,第二部分在handle_cast/2
或handle_info/2
中。实际上只有一个原因,那就是预计初始化需要很长时间。然后拆分它将允许gen_server:start_link
更快地返回,这对于主管启动的服务器很重要,因为他们在启动他们的孩子时“挂起”,而一个缓慢启动的孩子可以延迟整个主管启动。
在这种情况下,我不认为拆分服务器初始化是不好的方式。
小心错误很重要。 init/1
中的错误将导致主管在第二部分发生错误时终止,因为它们会导致主管尝试重新启动该子项。
我个人认为服务器向自己发送消息的方式更好,可以使用明确的!
或gen_server:cast
,也可以使用良好的描述性消息,例如{{1} ,更容易看到发生了什么,而不是更匿名的超时。特别是如果在其他地方也使用超时。
答案 2 :(得分:6)
打电话给你自己的主管确实看起来不错,但我总是做类似的事情。
init(...) ->
gen_server:cast(self(), startup),
{ok, ...}.
handle_cast(startup, State) ->
slow_initialisation_task_reading_from_disk_fetching_data_from_network_etc(),
{noreply, State}.
我认为这比使用timeout和handle_info更清楚,它几乎可以保证没有消息可以超出启动消息(在我们发送消息之前没有其他人拥有我们的pid),而且它没有如果我需要将超时用于其他事情,那就会妨碍你。
答案 3 :(得分:0)
这可能是非常有效和简单的解决方案,但我认为它不是好的erlang风格。 我正在使用计时器:apply_after,这是更好的,并没有给与外部模块/ gen _ *交互的印象。
我认为最好的方法是使用状态机(gen_fsm)。我们的大多数gen_srvers都是状态机,但是因为设置get_fsm的初始工作量我认为我们最终会使用gen_srv。
总而言之,我会使用timer:apply_after使代码清晰有效或gen_fsm成为纯Erlang风格(甚至更快)。
我刚刚阅读了代码片段,但是示例本身在某种程度上被打破了 - 我不理解gen_srv操作管理器的这种构造。即使它是未来一些孩子的经理,这也是明确做到这一点的更重要的原因,而不依赖于进程的邮箱魔术。调试这个在一些更大的系统中也是地狱。
答案 4 :(得分:0)
坦率地说,我没有看到拆分初始化的重点。在init
中执行繁重操作会导致主管挂起,但使用timeout/handle_info
,向self()
发送消息或向每个处理程序添加init_check
(另一种可能性,但不是很方便)将有效挂起呼叫过程。那么为什么我需要“工作”主管“不太正常”gen_server?清理实现应该包括初始化期间任何消息的“not_ready”回复(为什么不在完成时从init +发送完全初始化消息发送回self()
,这将重置“not_ready”状态),但是“未准备就绪” “回复应该由调用者正确处理,这增加了很多复杂性。暂停回复不是一个好主意。