亚当的回答很好,但我有一点要补充
使用 handle_call 将在调用期间阻塞进程。
对于进行 handle_call 调用的客户端来说,这始终是正确的。这花了我一段时间来回想一下,但这并不一定意味着 gen_server 在回答 handle_call 时也必须阻塞。
就我而言,当我创建一个处理 gen_server 的数据库并故意编写一个执行的查询时遇到了这种情况SELECT pg_sleep(10)
,这是 PostgreSQL 所说的“睡眠 10 秒”,这是我测试非常昂贵的查询的方式。我的挑战:我不希望数据库 gen_server 坐在那里等待数据库完成!
我的解决方案是使用gen_server:reply/2:
当无法在 Module:handle_call/3 的返回值中定义回复时,gen_server 可以使用此函数显式向调用 call/2,3 或 multi_call/2,3,4 的客户端发送回复。
在代码中:
-module(database_server).
-behaviour(gen_server).
-define(DB_TIMEOUT, 30000).
<snip>
get_very_expensive_document(DocumentId) ->
gen_server:call(?MODULE, {get_very_expensive_document, DocumentId}, ?DB_TIMEOUT).
<snip>
handle_call({get_very_expensive_document, DocumentId}, From, State) ->
%% Spawn a new process to perform the query. Give it From,
%% which is the PID of the caller.
proc_lib:spawn_link(?MODULE, query_get_very_expensive_document, [From, DocumentId]),
%% This gen_server process couldn't care less about the query
%% any more! It's up to the spawned process now.
{noreply, State};
<snip>
query_get_very_expensive_document(From, DocumentId) ->
%% Reference: http://www.erlang.org/doc/man/proc_lib.html#init_ack-1
proc_lib:init_ack(ok),
Result = query(pgsql_pool, "SELECT pg_sleep(10);", []),
gen_server:reply(From, {return_query, ok, Result}).