2013-11-15 2 views
0

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

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

Возможно ли использовать handle_info в некотором роде?

С уважением /Peter

ответ

5

Способ сделать это, чтобы сохранить прочь ссылку на отправителя в 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; значения вызовов функций и возврата аналогичны.

+0

Спасибо! Это именно то, что мне нужно. Я полностью забыл о {noreply, ...} "return". – peitur

+1

Будьте осторожны, так как «gen_server: call» имеет встроенный тайм-аут в 5 секунд, после которого автоматически генерируется ошибка. Длительность таймаута может быть задана с помощью третьего аргумента: миллисекунды или бесконечности. – rvirding

+0

Yup, я столкнулся с этим вопросом в предыдущем приложении. Теперь у меня обычно есть либо бесконечность, либо я передаю значение тайм-аута (которое предназначено для обратного вызова). Это делает вещи немного «грязными», хотя :( – peitur

Смежные вопросы