Erlang gen_server是否不能调用自身API?书中表述与示例代码矛盾疑问
你的理解存在偏差,二者不存在矛盾,核心区别在于调用的目标进程、调用类型和触发场景。
先明确书里提示的死锁触发前提
服务端不能调用自身的API函数,特指tr_server进程在自身回调逻辑执行过程中,主动发起对自己的同步
gen_server:call请求
gen_server的所有回调(handle_info/2、handle_call/3、handle_cast/2等)都是串行执行的,同一时间只会运行一个回调逻辑,新接收的消息会暂存到进程邮箱排队,等当前回调执行完成后才会取出下一条处理。
如果在handle_info/2的执行过程中调用tr_server:get_count(),会触发gen_server:call(?SERVER, get_count):
- 首先会给tr_server进程(就是当前正在执行回调的进程自身)的邮箱塞一条call消息
- 之后
gen_server:call会进入阻塞等待状态,等待目标进程返回结果
但此时tr_server进程还卡在handle_info/2的执行逻辑里,根本不会去处理邮箱里刚塞入的call消息,永远等不到返回,最终形成死锁。
再看示例代码的逻辑,和提示完全一致
示例中的do_rpc是处理客户端发来的RPC请求,执行apply(M,F,A)的时候分三种场景:
- 调用其他服务/普通模块的函数:完全没有问题,不涉及自身进程的调用,不会触发死锁
- 调用tr_server自身的异步API(比如
tr_server:stop/0,内部是gen_server:cast):不会死锁,因为cast是异步操作,只负责把消息塞到自身邮箱,不会阻塞等待返回,当前handle_info/2执行完成后,就会自动取出cast消息处理 - 调用tr_server自身的同步API(也就是书里明确列为例外的
tr_server:get_count/0):确实会触发死锁,这也是为什么书里专门强调这个接口不允许被RPC调用,属于明确禁止的例外场景,和提示完全吻合,不存在矛盾。
内容的提问来源于stack exchange,提问作者gy l
相关产品推荐
相关产品推荐

