Dav*_*don 15 erlang erlang-otp
在此示例中,作者通过执行以下操作来避免死锁情况:
self() ! {start_worker_supervisor, Sup, MFA}
在他的gen_server的init函数中.我在我的一个项目中做了类似的事情,并被告知这种方法不受欢迎,而且最好立即引起超时.什么是可接受的模式?
Ada*_*erg 14
考虑使用新gen_statem行为.此行为支持生成FSM内部的事件:
state函数可以使用action()next_event插入事件,并将此类事件作为当前状态插入到state函数中.也就是说,好像它是最老的传入事件.专用的event_type()内部可用于此类事件,使其无法对外部事件进行错误处理.
插入事件取代了调用自己通常不得不求助的状态处理函数的技巧,例如,gen_fsm强制在其他事件之前处理插入的事件.
使用该模块中的操作功能,您可以确保init在任何外部事件之前生成并始终处理事件,特别是通过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) ->
...
...
Run Code Online (Sandbox Code Playgroud)
在设计时,gen_server您通常可以选择在三种不同的状态下执行操作:
init/1handle_*功能中运行时terminate/2一个好的经验法则是在处理事件(调用,强制转换,消息等)时执行处理函数中的事物.在init中执行的东西不应该等待事件,这就是句柄回调的用途.
因此,在这种特殊情况下,会产生一种"假"事件.我说似乎gen_server总是想要启动主管的启动.为什么不直接进行init/1呢?是否真的需要能够处理中间的另一条消息(handle_info/2相反,这样做的效果)?那个窗口非常小(从开始gen_server发送消息到发送消息之间的时间self())所以它根本不太可能发生.
至于死锁,我真的建议不要在你的init函数中调用你自己的主管.这只是不好的做法.启动工人流程的良好设计模式将是一个顶级主管,下面是经理和工人主管.经理通过致电工人主管来启动工人:
[top_sup]
| \
| \
| \
man [work_sup]
/ | \
/ | \
/ | \
w1 ... wN
Run Code Online (Sandbox Code Playgroud)
只是为了补充已经说过的关于将服务器初始化分成两部分的内容,第一部分在init/1函数中,第二部分在handle_cast/2或者handle_info/2.实际上只有一个原因,那就是预计初始化需要很长时间.然后将其拆分将允许gen_server:start_link更快地返回,这对于由主管启动的服务器而言是重要的,因为他们在启动他们的孩子时"挂起"并且一个缓慢启动的孩子可以延迟整个主管启动.
在这种情况下,我不认为分割服务器初始化是不好的风格.
谨慎处理错误非常重要.错误init/1将导致主管在第二部分中发生错误时终止,因为它们将导致主管尝试重新启动该子项.
我个人认为服务器向自己发送消息是更好的方式,可以使用显式!或a gen_server:cast,以及良好的描述性消息,例如init_phase_2,它将更容易看到发生了什么,而不是更匿名超时.特别是如果在其他地方也使用超时.
打电话给你自己的主管确实看起来不错,但我总是做类似的事情.
init(...) ->
gen_server:cast(self(), startup),
{ok, ...}.
handle_cast(startup, State) ->
slow_initialisation_task_reading_from_disk_fetching_data_from_network_etc(),
{noreply, State}.
Run Code Online (Sandbox Code Playgroud)
我认为这比使用timeout和handle_info更清楚,它几乎可以保证没有消息可以超出启动消息(在我们发送消息之前没有其他人有我们的pid),并且它没有进入如果我需要使用超时来做别的事情.