Dog*_*Dog 14 concurrency erlang
我偶尔会看到一种模式init/1
,即gen_server
进程的功能会向自己发送一条消息,表明它应该被初始化.这样做的目的是让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.
Run Code Online (Sandbox Code Playgroud)
该过程假定init
在任何其他消息之前将收到消息 - 否则它将崩溃.此过程是否有可能在m
消息之前获取init
消息?
假设没有进程将消息发送到由此生成的随机pid list_to_pid
,因为执行此操作的任何应用程序可能根本不起作用,无论此问题的答案如何.
这个问题的理论答案有可能是一个过程,在init消息之前得到的消息?是YES.但实际上(当没有进程正在执行list_to_pid并发送消息时)此过程的答案是NO,前提是gen_server不是注册进程.
这是因为gen_server:start_link的返回确保执行gen_server的回调init.因此,在任何其他进程获得Pid发送消息之前,初始化消息是进程消息队列中的第一条消息.因此,您的进程是安全的,并且在init之前不会收到任何其他消息.
但是对于注册过程也不会这样,因为可能存在一个进程,即使在完成回调init函数之前,也可能使用注册名称向gen_server发送消息.让我们考虑一下这个测试功能.
test() ->
Times = lists:seq(1,1000),
spawn(gen_server, start_link,[{local, ?MODULE}, ?MODULE, [], []]),
[gen_server:cast(?MODULE, No) || No <-Times].
Run Code Online (Sandbox Code Playgroud)
样本输出是
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
...
Run Code Online (Sandbox Code Playgroud)
您可以看到gen_server在初始化之前收到356到385条消息的消息.因此,异步回调在注册名称方案中不起作用.
这可以通过两种方式解决
1.返回Pid后注册进程.
Run Code Online (Sandbox Code Playgroud)start_link_reg() -> {ok, Pid} = gen_server:start(?MODULE, [], []), register(?MODULE, Pid).
2.或者在handle_cast中为init消息注册该进程.
Run Code Online (Sandbox Code Playgroud)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
...
Run Code Online (Sandbox Code Playgroud)
因此,向自己发送消息以进行初始化并不能确保它是它收到的第一条消息,但代码(和设计)中的一些变化可以确保它是第一个被执行的消息.