在init中向self()发送消息是不是很糟糕?

Dav*_*don 15 erlang erlang-otp

示例中,作者通过执行以下操作来避免死锁情况:

self() ! {start_worker_supervisor, Sup, MFA}

在他的gen_server的init函数中.我在我的一个项目中做了类似的事情,并被告知这种方法不受欢迎,而且最好立即引起超时.什么是可接受的模式?

Ada*_*erg 14

Erlang 19+的更新

考虑使用新gen_statem行为.此行为支持生成FSM内部的事件:

state函数可以使用action()next_event插入事件,并将此类事件作为当前状态插入到state函数中.也就是说,好像它是最老的传入事件.专用的event_type()内部可用于此类事件,使其无法对外部事件进行错误处理.

插入事件取代了调用自己通常不得不求助的状态处理函数的技巧,例如,gen_fsm强制在其他事件之前处理插入的事件.

使用该模块中的操作功能,您可以确保init在任何外部事件之前生成并始终处理事件,特别是通过next_eventinit函数中创建操作.

例:

...

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/1
  • 在任何handle_*功能中运行时
  • 停下来的时候 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)


rvi*_*ing 7

只是为了补充已经说过的关于将服务器初始化分成两部分的内容,第一部分在init/1函数中,第二部分在handle_cast/2或者handle_info/2.实际上只有一个原因,那就是预计初始化需要很长时间.然后将其拆分将允许gen_server:start_link更快地返回,这对于由主管启动的服务器而言是重要的,因为他们在启动他们的孩子时"挂起"并且一个缓慢启动的孩子可以延迟整个主管启动.

在这种情况下,我不认为分割服务器初始化是不好的风格.

谨慎处理错误非常重要.错误init/1将导致主管在第二部分中发生错误时终止,因为它们将导致主管尝试重新启动该子项.

我个人认为服务器向自己发送消息是更好的方式,可以使用显式!或a gen_server:cast,以及良好的描述性消息,例如init_phase_2,它将更容易看到发生了什么,而不是更匿名超时.特别是如果在其他地方也使用超时.


cth*_*ops 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}.
Run Code Online (Sandbox Code Playgroud)

我认为这比使用timeout和handle_info更清楚,它几乎可以保证没有消息可以超出启动消息(在我们发送消息之前没有其他人有我们的pid),并且它没有进入如果我需要使用超时来做别的事情.

  • 如果你的`gen_server`是用`gen_server:start_link/4`或`gen_server:start/4`开始的,那么注册就会在调用`Module:init/1`之前发生.任何在其注册名称下查找`gen_server`的人都可以找到它并向它发送一条在你自己之前到达的消息. (3认同)