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; вызовы функций и возвращаемые значения похожи.