Erlang смешивая синхронизирующие и асинхронные функции

Можно ли заблокировать синхронную функцию (handle_call) для ожидания асинхронного вызова (handle_cast) в другом модуле?

т.е. как я могу заставить синхронную функцию ждать (т.е. блокировать), пока не будет получено конкретное сообщение "сделано" от асинхронной функции? (и только затем продолжить выполнение) Оба модуля следуют поведению gen_server и gen_fsm.

Можно ли использовать handle_info каким-либо образом?

С уважением / Питер

1 ответ

Решение

Способ сделать это - сохранить ссылку на отправителя в handle_callи позвоните gen_server:reply на более позднем этапе. То есть, пока вы обычно пишете свой handle_call функционировать так:

handle_call(foo, _From, State) ->
    {reply, {ok, bar}, State}.

вы бы сделали что-то вроде этого:

handle_call(foo, From, State = #state{pending = Pending}) ->
    NewState = State#state{pending = [From | Pending]},
    {noreply, NewState}.

А потом, возможно, в handle_info:

handle_info({bar_event, Data}, State = #state{pending = [Head | Tail]) ->
    gen_server:reply(Head, {ok, Data}),
    NewState = State#state{pending = Tail},
    {noreply, NewState}.

Это означает, что когда процесс вызывает gen_server:call(Pid, foo)будет блокироваться до тех пор, пока процесс сервера не получит {bar_event, Data}в этот момент сервер вернет данные, содержащиеся в событии, вызывающей стороне.

Я постарался сохранить пример простым, сохраняя информацию о вызывающих пользователях в "стеке", поэтому в случае нескольких одновременных вызывающих абонентов последний вызывающий будет возвращаться первым, и наоборот. Во многих случаях вы захотите сохранить информацию о вызывающем абоненте с ключом, который вы можете просмотреть, когда получите асинхронное событие.

Это также можно сделать в процессе gen_fsm; вызовы функций и возвращаемые значения похожи.

Другие вопросы по тегам