Erlang gen_server具有长时间运行的任务

时间:2012-01-16 11:11:51

标签: 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}.

但是当这样的任务运行时,整个服务器都没有响应,任何对它的调用都会导致整个服务器崩溃:

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}

2 个答案:

答案 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)的唯一方法是不在其上运行阻塞任务。所以另一种选择可能是在一个只与您的服务器对话的不同进程上运行您的长任务,所以没有人关心它被阻止。