Concurrency 如果gen_服务器进程中的init/1函数向自身发送一条消息,是否保证它在任何其他消息之前到达?
我偶尔会看到一种模式,Concurrency 如果gen_服务器进程中的init/1函数向自身发送一条消息,是否保证它在任何其他消息之前到达?,concurrency,erlang,Concurrency,Erlang,我偶尔会看到一种模式,gen_服务器的init/1函数将向自身发送一条消息,表示应该初始化它。这样做的目的是让gen_server进程异步初始化自身,以便生成它的进程不必等待。以下是一个例子: -module(test). -compile(export_all). init([]) -> gen_server:cast(self(), init), {ok, {}}. handle_cast(init, {}) -> io:format("initial
gen_服务器的init/1
函数将向自身发送一条消息,表示应该初始化它。这样做的目的是让gen_server
进程异步初始化自身,以便生成它的进程不必等待。以下是一个例子:
-module(test).
-compile(export_all).
init([]) ->
gen_server:cast(self(), init),
{ok, {}}.
handle_cast(init, {}) ->
io:format("initializing~n"),
{noreply, lists:sum(lists:seq(1,10000000))};
handle_cast(m, X) when is_integer(X) ->
io:format("got m. X: ~p~n", [X]),
{noreply, X}.
b() ->
receive P -> {} end,
gen_server:cast(P, m),
b().
test() ->
B = spawn(fun test:b/0),
{ok, A} = gen_server:start_link(test,[],[]),
B ! A.
该过程假定init
消息将在任何其他消息之前被接收,否则它将崩溃。此过程是否可能在init
消息之前获取m
消息
让我们假设没有进程将消息发送到由list\u to\u pid
生成的随机pid,因为不管这个问题的答案如何,任何这样做的应用程序都可能根本无法工作。gen\u server使用proc\u lib:init\u ack确保进程在从start\u链接返回pid之前正确启动。因此,在init中发送的消息将是第一条消息 在这种特殊情况下,您可以安全地假设“init”消息将在“m”之前收到。一般来说(尤其是如果您注册了流程),这是不正确的
如果您想100%安全地知道您的初始化代码将首先运行,您可以执行以下操作:
start_link(Args...) ->
gen_server:start_link(test, [self(), Args...], []).
init([Parent, Args...]) ->
do_your_synchronous_start_stuff_here,
proc_lib:init_ack(Parent, {ok, self()}),
do_your_async_initializing_here,
io:format("initializing~n"),
{ok, State}.
我没有对此进行测试,所以我不知道“奖金”初始确认是否会向终端打印丑陋的消息。如果是这样,代码必须稍微扩展,但总体思路仍然有效。让我知道,我会更新我的答案。这不是100%安全!
在gen.erl
第117-129行中,我们可以看到:
init_it(GenMod, Starter, Parent, Mod, Args, Options) ->
init_it2(GenMod, Starter, Parent, self(), Mod, Args, Options).
init_it(GenMod, Starter, Parent, Name, Mod, Args, Options) ->
case name_register(Name) of
true ->
init_it2(GenMod, Starter, Parent, Name, Mod, Args, Options);
{false, Pid} ->
proc_lib:init_ack(Starter, {error, {already_started, Pid}})
end.
init_it2(GenMod, Starter, Parent, Name, Mod, Args, Options) ->
GenMod:init_it(Starter, Parent, Name, Mod, Args, Options).
在init_it/7
中,进程首先注册其名称,然后在init_it2/7
中调用GenMod:init_it/6
,在其中调用您的init/1
函数
test() ->
Times = lists:seq(1,1000),
spawn(gen_server, start_link,[{local, ?MODULE}, ?MODULE, [], []]),
[gen_server:cast(?MODULE, No) || No <-Times].
虽然在gen_server:start_链接返回之前,很难猜出新的进程id。但是,如果您向服务器发送一条带有注册名称的消息,并且该消息在调用gen_server:cast之前到达,那么您的代码将是错误的
Daniel的解决方案可能是正确的,但我不确定两个proc_lib:init_ack
是否会导致错误。但是,家长永远不希望收到意外消息。>_我是这里的一名新生,我多么希望能添加一条评论。>“您的示例代码是安全的,m
总是在init
之后收到
但是,从理论上看,如果gen_服务器的init/1
处理程序使用gen_服务器:cast/2
或send原语向自身发送消息,则不能保证它是第一条消息
无法保证这一点,因为init/1
是在gen_服务器的进程内执行的,因此在创建进程并分配pid和邮箱之后。在非SMP模式下,调度程序可以在调用init函数之前或消息发送之前,在一定负载下调度进程已发送,因为调用函数(例如gen_server:cast/2
或该问题的init处理程序)会生成一个缩减,并且BEAM emulator会测试是否应该给其他进程一些时间。在SMP模式下,您可以让另一个调度程序运行一些代码,并向您的进程发送消息
理论与实践的区别在于找出进程存在的方式(以便在init
消息之前向其发送消息)。代码可以使用来自主管的链接、注册名称、由erlang:processs()返回的进程列表
甚至可以使用随机值调用list_to_pid/1
,或者使用binary_to_term/1
取消pid序列化。您的节点甚至可能会从另一个具有序列化pid的节点收到消息,特别是考虑到创建编号在3之后(请参阅其他问题)
这在实践中不太可能。因此,从实际的角度来看,每次使用此模式时,代码都可以设计为确保首先接收init
消息,并且在服务器接收其他消息之前初始化服务器
如果gen_服务器是一个已注册的进程,您可以从一个管理器启动它,并确保所有客户端随后在管理树中启动,或者引入某种(可能较差的)同步机制。即使您不使用这种异步初始化模式,这也是必需的(否则客户端无法访问服务器)。当然,如果此gen_服务器崩溃和重新启动,您可能仍然会遇到问题,但无论在何种情况下都是如此,您只能通过精心编制的监控树来保存
如果gen_服务器未注册或未按名称引用,客户端最终将pid传递到gen_服务器:call/2,3
或gen_服务器:cast/2
,它们将通过调用gen_服务器:start_link/3
gen_服务器:start_link/3
的主管获得该pid,仅在init>时返回/1
返回,因此在init
消息排队后返回。这正是您上面的代码所做的。问题的理论答案a进程是否可以在init消息之前获取消息?isYES。
但实际上
1> async_init:test().
Received:356
Received:357
[ok,ok,ok,ok,ok,ok,ok,ok,ok,ok,ok,ok,ok,ok,ok,ok,ok,ok,ok,
ok,ok,ok,ok,ok,ok,ok,ok,ok,ok|...]
Received:358
Received:359
2> Received:360
2> Received:361
...
2> Received:384
2> Received:385
2> Initializing
2> Received:386
2> Received:387
2> Received:388
2> Received:389
...
start_link_reg() ->
{ok, Pid} = gen_server:start(?MODULE, [], []),
register(?MODULE, Pid).
handle_cast(init, State) ->
register(?MODULE, self()),
io:format("Initializing~n"),
{noreply, State};
1> async_init:test().
Initializing
Received:918
Received:919
[ok,ok,ok,ok,ok,ok,ok,ok,ok,ok,ok,ok,ok,ok,ok,ok,ok,ok,ok,
ok,ok,ok,ok,ok,ok,ok,ok,ok,ok|...]
Received:920
2> Received:921
2> Received:922
...