Вызов handle_info из порядка выполнения handle_cast / handle_call - PullRequest
0 голосов
/ 03 мая 2020

У меня есть gen_server, и я не понимаю следующее:

-module(myserver).
-behaviour(gen_server).
-record(state,{ count=0}).

handle_cast(Message,From,State=#state{count=C})->
     self() ! something,
     {noreply,State}.
handle_info(Message,State=#state{count=C})
      NewCount=case Message of 
                   0 -> C+1;
                   true ->C,
      {noreply,State#state{count=NewCount}}.

Учитывая приведенный выше код, я хочу понять следующее:
- при условии, что я ввожу handle_cast и я отправьте что-то на mailbox gen_server, что соответствует случаю handle_info, будет ли handle_info вызываться в другом потоке тут же и там (self() ! Message, пока я все еще оцениваю handle_cast?

В каком порядке разворачиваются вещи?

  • A

    • handle_cast введите
    • handle_cast отправить в собственное почтовый ящик
    • handle_cast возвращается
    • handle_info срабатывает
  • B

    1. handle_cast введите
    2. handle_cast отправить на собственный почтовый ящик
      3. handle_info запускается (одновременно) и может изменить состояние
    3. handle_cast может закончиться или не закончиться до завершения handle_info и может изменить состояние на handle_info

Ответы [ 2 ]

2 голосов
/ 04 мая 2020

Ответ А. Все обработчики gen_server - например, handle_cast или handle_info - всегда выполняются в одном и том же процессе. Сообщение, отправленное самому себе в handle_cast, будет получено реализацией gen_server после возврата обратного вызова handle_cast. Только тогда будет вызван handle_info, и handle_info получит состояние, возвращенное от handle_cast.

Как правило, все обработчики gen_server всегда вызываются последовательно, и состояние, возвращаемое обработчиком, передается к обработчику, который вызывается следующим.

1 голос
/ 04 мая 2020

Вот пример, который демонстрирует, что ответ Wotek Surowka является правильным:

-module(s1).
-behavior(gen_server).
-compile(export_all).

start() ->
    gen_server:start_link(
      {local, ?MODULE}, 
      ?MODULE, 
      [],
      []
    ).

init(_Args) ->
    Count = 0,
    {ok, Count}.

handle_call(_Msg, _From, State) ->
    {reply, hello, State}.

handle_cast(_Msg, Count) ->
    io:format("Entered handle_cast()...~n"),

    self() ! hello,
    timer:sleep(10000), % Sleep for 10 seconds

    io:format("Returning from handle_cast()...~n"),
    {noreply, Count+1}.

handle_info(Msg, Count) ->
    io:format("Entered handle_info(), Msg= ~w~n", [Msg]),
    io:format("Count in handle_info() is: ~w~n", [Count]),
    io:format("Returning from handle_info()...~n"),
    {noreply, Count}.

go() ->
    spawn(
      fun() -> gen_server:cast(?MODULE, "some message") end
    ),
    ok.

В приведенном выше примере handle_cast() спит в течение 10 секунд, поэтому, если handle_info() выполняется асинхронно, у него будет много время, чтобы отобразить его вывод, прежде чем handle_cast() вернулся. Вот результаты в оболочке:

~/erlang_programs/gen_s/1server$ erl
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V9.3  (abort with ^G)

1> c(s1).     
s1.erl:3: Warning: export_all flag enabled - all functions will be exported
{ok,s1}

2> s1:start().
{ok,<0.71.0>}

3> s1:go().   
Entered handle_cast()...
ok  <--- return value of go()
Returning from handle_cast()...
Entered handle_info(), Msg= hello
Count in handle_info() is: 1
Returning from handle_info()...

4> 

Вывод показывает, что handle_info() не начинает выполняться, пока не вернется handle_cast().

И, если вы добавите несколько операторов печати чтобы отобразить pid внутри handle_cast() и handle_info(), вы увидите, что pid одинаков:

-module(s1).
-behavior(gen_server).
-compile(export_all).

start() ->
    gen_server:start_link(
      {local, ?MODULE}, 
      ?MODULE, 
      [],
      []
    ).

init(_Args) ->
    Count = 0,
    {ok, Count}.

handle_call(_Msg, _From, State) ->
    {reply, hello, State}.

handle_cast(_Msg, Count) ->
    io:format("Entered handle_cast()...~n"),
    Self = self(),
    io:format("self() is: ~w~n", [Self]),

    Self ! hello,
    timer:sleep(10000), % Sleep for 10 seconds

    io:format("Returning from handle_cast()...~n"),
    {noreply, Count+1}.

handle_info(Msg, Count) ->
    io:format("Entered handle_info(), Msg= ~w~n", [Msg]),
    io:format("self() is: ~w~n", [self()]),
    io:format("Count in handle_info() is: ~w~n", [Count]),
    io:format("Returning from handle_info()...~n"),
    {noreply, Count}.

go() ->
    spawn(
      fun() -> gen_server:cast(?MODULE, "some message") end
    ),
    ok.

В оболочке:

1> c(s1).     
s1.erl:3: Warning: export_all flag enabled - all functions will be exported
{ok,s1}

2> s1:start().
{ok,<0.71.0>}

3> s1:go().   
Entered handle_cast()...
ok  <---return value of go()
self() is: <0.71.0>
Returning from handle_cast()...
Entered handle_info(), Msg= hello
self() is: <0.71.0>
Count in handle_info() is: 1
Returning from handle_info()...

4> 
...