美好的一天,
我有一个gen_server
进程,可以定期执行一些长时间运行的状态更新任务
handle_info
:
handle_info(trigger, State) ->
NewState = some_long_running_task(),
erlang:send_after(?LOOP_TIME, self(), trigger),
{noreply, NewState}.
但是当这样的任务运行时,整个服务器都没有响应,任何对它的调用都会导致整个服务器崩溃:
my_gen_server:status().
** exception exit: {timeout,{gen_server,call,[my_gen_server,status]}}
in function gen_server:call/2
如何避免阻止gen_server?
当一个人随时打my_gen_server:status()
时,结果应该是这样的:
{ok, task_active}
答案 0 :(得分:13)
在单独的进程中执行长时间运行的任务。让这个过程通知gen_server其任务的进度(即是否可以跟踪任务的进度)或让进程完成任务或失败但至少通知gen_server任务结果。
让gen_server与执行此长期运行任务的进程链接,并让gen_server知道PID或注册名称,以便在退出信号的情况下,它可以将该重要进程的死亡与Rest隔离开来。
handle_info(trigger, State) -> Pid = spawn_link(?MODULE,some_long_running_task,[State]), NewState = save_pid(Pid,State), {noreply, NewState}; handle_info({'EXIT',SomePid,_},State)-> case lookup_pid(State) == SomePid of false -> %% some other process {noreply,State}; true -> %% our process has died %% what do we do now ? %% spawn another one ? %% thats your decision to take .... .... {noreply,State} end; handle_info({finished,TaskResult},State)-> .....%% update state e.t.c. erlang:send_after(?LOOP_TIME, self(), trigger), {noreply,NewState}. some_long_running_task(ServerState)-> ....do work ....return results
答案 1 :(得分:5)
此调用不导致崩溃,但只是导致可以捕获的异常:
status() ->
try gen_server:call(my_gen_server, status)
catch
exit:{timeout,_} -> {ok, task_active}
end.
但是,呼叫将保留在服务器的队列中,在完成处理当前消息后,它将发送一条回复消息:{ServerRef, Reply}
,应由调用进程丢弃。
避免阻止Erlang中的任何进程(无论是否gen_server
)的唯一方法是不在其上运行阻塞任务。所以另一种选择可能是在一个只与您的服务器对话的不同进程上运行您的长任务,所以没有人关心它被阻止。