Adam的回答很好,但我有一点要补充。
使用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) ->
proc_lib:spawn_link(?MODULE, query_get_very_expensive_document, [From, DocumentId]),
{noreply, State};
<snip>
query_get_very_expensive_document(From, DocumentId) ->
proc_lib:init_ack(ok),
Result = query(pgsql_pool, "SELECT pg_sleep(10);", []),
gen_server:reply(From, {return_query, ok, Result}).