Dfr*_*Dfr 7 erlang erlang-otp gen-server
美好的一天,
我有一个gen_server
进程,它定期执行一些长期运行的状态更新任务
handle_info
:
handle_info(trigger, State) ->
NewState = some_long_running_task(),
erlang:send_after(?LOOP_TIME, self(), trigger),
{noreply, NewState}.
Run Code Online (Sandbox Code Playgroud)
但是当这样的任务运行时,整个服务器都没有响应,任何对它的调用都会导致整个服务器崩溃:
my_gen_server:status().
** exception exit: {timeout,{gen_server,call,[my_gen_server,status]}}
in function gen_server:call/2
Run Code Online (Sandbox Code Playgroud)
如何避免阻止gen_server?当一个人my_gen_server:status()
随时打电话时,结果应该是这样的:
{ok, task_active}
Muz*_*hua 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
这个调用不会导致崩溃,而仅仅是为了可捕获的异常:
status() ->
try gen_server:call(my_gen_server, status)
catch
exit:{timeout,_} -> {ok, task_active}
end.
Run Code Online (Sandbox Code Playgroud)
但是,呼叫将保留在服务器的队列中,在完成当前消息的处理后,它将发送一个应答消息:{ServerRef, Reply}
,应由调用进程丢弃.
避免阻止Erlang中的任何进程(无论是否gen_server
)的唯一方法是不在其上运行阻塞任务.所以另一种选择可能是在一个只与你的服务器对话的不同进程上运行你的长任务,所以没有人关心它被阻止了.