You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用gen_server:start_link创建的进程未随父进程终止问题排查

Erlang gen_server未随父进程终止的问题排查与解决

问题场景

编写了如下trivial模块的gen_server:

-module(trivial).
-behaviour(gen_server).
-export([
    init/1,
    handle_call/3,
    handle_cast/2
]).

init([]) -
    {ok, []}.

handle_call(_Msg, _From, State) -
    {reply, ok, State}.

handle_cast(_Msg, State) -
    {noreply, State}.

接着在一个会立即终止的进程中启动它:

c(trivial).

Before = length([ Proc || Proc <- processes(), is_process_alive(Proc) ]).

Pid = spawn(fun() - gen_server:start_link(trivial, [], []) end).

false = is_process_alive(Pid).

Before = length([ Proc || Proc <- processes(), is_process_alive(Proc) ]).

执行后最后一个断言不成立:存活进程数多了1个。预期gen_server:start_link会在spawn出的进程与服务器进程间创建链接,当spawn进程终止时服务器也随之终止,但实际并未发生。

原因分析

  • gen_server:start_link/3确实会在调用进程(即spawn出的进程)和gen_server进程之间建立链接,但Erlang的链接机制有个关键规则:只有当链接的进程因非正常原因(如崩溃、被exit(kill)杀死)终止时,才会触发关联进程的退出;如果进程是正常终止(退出原因为normal),链接的进程不会收到退出信号,会继续存活。
  • 这里spawn出的进程在gen_server:start_link成功返回后,没有后续代码执行,会以normal原因正常终止,因此gen_server进程不会被触发退出,导致存活进程数增加。
  • 另外,你的gen_server模块缺少了handle_info/2、terminate/2等必要回调的导出,虽然不影响当前问题,但不符合gen_server的规范实现。

解决方法

要实现“spawn进程终止时gen_server随之终止”的需求,有两种可行方式:

方式1:让spawn进程以非正常原因终止

在spawn的函数体中,调用gen_server:start_link后主动让进程以非normal原因退出,比如使用exit(kill):

Pid = spawn(fun() -> gen_server:start_link(trivial, [], []), exit(kill) end).

此时spawn进程以kill原因终止,链接的gen_server进程会收到退出信号,随之终止。

方式2:让gen_server监听父进程退出事件

修改gen_server的实现,开启退出陷阱并处理父进程的退出消息,确保父进程无论以何种原因终止,gen_server都随之退出:

-module(trivial).
-behaviour(gen_server).
-export([
    init/1,
    handle_call/3,
    handle_cast/2,
    handle_info/2,
    terminate/2
]).

init([]) ->
    % 开启退出陷阱,让进程能收到EXIT消息
    process_flag(trap_exit, true),
    % 获取并链接父进程
    Parent = erlang:group_leader(),
    link(Parent),
    {ok, []}.

handle_call(_Msg, _From, State) ->
    {reply, ok, State}.

handle_cast(_Msg, State) ->
    {noreply, State}.

handle_info({'EXIT', _ParentPid, _Reason}, State) ->
    % 父进程退出,终止自身
    {stop, normal, State};
handle_info(_Info, State) ->
    {noreply, State}.

terminate(_Reason, _State) ->
    ok.

这种方式更优雅,能处理父进程正常或非正常终止的所有情况,确保gen_server随父进程一起退出。

内容的提问来源于stack exchange,提问作者Tim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 08:45:37