7

再会,

我有一个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}

4

2 回答 2

13

在单独的进程中执行长时间运行的任务。让这个进程通知 gen_server 它的任务进度(即是否可以跟踪任务的进度)或者让进程完成任务或失败,但至少通知 gen_server 任务的结果。

让 gen_server 与执行这个长时间运行任务的进程链接,并让 gen_server 知道 PID 或注册名称,以便在退出信号的情况下,它可以将那个重要进程的死亡与 Rest 隔离开来。

处理信息(触发器,状态)->
    Pid = spawn_link(?MODULE,some_long_running_task,[State]),
    NewState = save_pid(Pid,State),
    {noreply,新州};
handle_info({'EXIT',SomePid,_},State)->
    case lookup_pid(State) == SomePid of
        false -> %% 其他进程
            {noreply,州};
        真->
            %% 我们的进程已经死亡
            %% 我们现在干什么 ?
            %% 生成另一个?
            %% 那是你的决定
            ……
            ……
            {noreply,州}
    结尾;
handle_info({finished,TaskResult},State)->
    .....%% 更新状态等
    erlang:send_after(?LOOP_TIME, self(), trigger),
    {noreply,新州}。

some_long_running_task(ServerState)->
    ....做工作
    ....返回结果
于 2012-01-16T12:14:26.293 回答
5

此调用不会导致崩溃,而只会导致可以捕获的异常:

status() ->
  try gen_server:call(my_gen_server, status)
  catch
    exit:{timeout,_} -> {ok, task_active}
  end.

但是,该调用将保留在服务器的队列中,并且在它处理完当前消息后,它会发送一个回复消息:{ServerRef, Reply},该消息应该被调用进程丢弃。

避免阻塞 Erlang 中任何进程(无论是否gen_server)的唯一方法是不在其上运行阻塞任务。因此,另一种选择可能是在仅与您的服务器对话的不同进程上运行您的长任务,因此没有人关心它是否被阻止。

于 2012-01-16T11:47:47.970 回答