为什么Erlang/OTPgen_server回调模块必须提供handle_cast功能?

Why Erlang/OTP gen_server callback module must provide handle_cast function?

我能理解为什么回调模块必须提供inithandle_call功能。 init 用于创建初始状态,handle_call 是创建服务器进程的主要目的:为请求提供服务。

但我不明白为什么需要 handle_castgen_server 模块不能提供默认实现,就像它为许多其他回调所做的那样?可能是一个空洞

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

在我看来,大多数回调模块无论如何都提供这样的 noops。

handle_cast 类似于 handle_call,用于异步调用 gen_server 你是 运行(调用是同步的)。它正在为您处理请求,只是不像 call 那样回复。

gen_call 类似,它可以改变您的 gen_server 的状态(或保持原样,取决于您的需要和实施)。它还可以像您的调用一样停止您的服务器、休眠等 - 请参阅 learn you some erlang 以获取示例和更广泛的解释。

它 "can be a noop" 如您在问题中所说,但在某些情况下,最好实施和处理对服务器的异步调用。

and handle_call is the main purpose for creating a server process: to serve requests.

客户端-服务器体系结构可以应用于更广泛的问题,而不仅仅是提供文档的 Web 服务器。一个例子是在几本 erlang 书籍中讨论的频率服务器。客户端可以向服务器请求频率以进行 phone 调用,然后客户端必须等待服务器提供 return 特定频率才能进行调用。这是一个典型的 gen_server:call() 情况:客户端必须等待服务器 return 一个频率,然后客户端才能进行 phone 调用。

但是,当客户端使用完频率后,客户端会向服务器发送一条消息,告知服务器取消分配频率。在那种情况下,客户端不需要等待服务器的响应,因为客户端甚至不关心服务器的响应是什么。客户端只需要发送deallocate消息,客户端就可以继续执行其他代码了。服务器有责任在有时间时处理解除分配消息,然后将频率从 "busy" 列表移动到 "free" 列表,以便其他客户端可以使用该频率。结果,客户端使用 gen_server:cast() 向服务器发送解除分配消息。

现在,频率服务器的"main purpose"是什么?分配或取消分配频率?如果服务器不取消分配频率,那么在一定数量的客户端请求之后,将不会再分发任何频率,客户端将收到一条消息 "no frequencies available"。因此,为了使系统正常工作,取消分配频率的行为是必不可少的。换句话说,handle_call() 不是服务器的 "main purpose"——handle_cast() 同样重要——需要两个处理程序来保持系统 运行 尽可能高效.

Couldn't gen_server module provide a default implementation, like it does for many other callbacks?

您为什么不能自己创建一个默认实现 handle_cast() 的 gen_server 模板?这是 emac 的默认 gen_server 模板:

-behaviour(gen_server).

%% API
-export([start_link/0]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
         terminate/2, code_change/3]).

-define(SERVER, ?MODULE).

-record(state, {}).

%%%===================================================================
%%% API
%%%===================================================================

%%--------------------------------------------------------------------
%% @doc
%% Starts the server
%%
%% @spec start_link() -> {ok, Pid} | ignore | {error, Error}
%% @end
%%--------------------------------------------------------------------
start_link() ->
    gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).

%%%===================================================================
%%% gen_server callbacks
%%%===================================================================

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Initializes the server
%%
%% @spec init(Args) -> {ok, State} |
%%                     {ok, State, Timeout} |
%%                     ignore |
%%                     {stop, Reason}
%% @end
%%--------------------------------------------------------------------
init([]) ->
    {ok, #state{}}.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Handling call messages
%%
%% @spec handle_call(Request, From, State) ->
%%                                   {reply, Reply, State} |
%%                                   {reply, Reply, State, Timeout} |
%%                                   {noreply, State} |
%%                                   {noreply, State, Timeout} |
%%                                   {stop, Reason, Reply, State} |
%%                                   {stop, Reason, State}
%% @end
%%--------------------------------------------------------------------
handle_call(_Request, _From, State) ->
    Reply = ok,
    {reply, Reply, State}.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Handling cast messages
%%
%% @spec handle_cast(Msg, State) -> {noreply, State} |
%%                                  {noreply, State, Timeout} |
%%                                  {stop, Reason, State}
%% @end
%%--------------------------------------------------------------------
handle_cast(_Msg, State) ->
    {noreply, State}.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Handling all non call/cast messages
%%
%% @spec handle_info(Info, State) -> {noreply, State} |
%%                                   {noreply, State, Timeout} |
%%                                   {stop, Reason, State}
%% @end
%%--------------------------------------------------------------------
handle_info(_Info, State) ->
    {noreply, State}.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any
%% necessary cleaning up. When it returns, the gen_server terminates
%% with Reason. The return value is ignored.
%%
%% @spec terminate(Reason, State) -> void()
%% @end
%%--------------------------------------------------------------------
terminate(_Reason, _State) ->
    ok.

%%--------------------------------------------------------------------
%% @private
%% @doc
%% Convert process state when code is changed
%%
%% @spec code_change(OldVsn, State, Extra) -> {ok, NewState}
%% @end
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

%%%===================================================================
%%% Internal functions
%%%===================================================================