アダムの答えは素晴らしいですが、追加する点が1つあります
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}).