为什么Erlang / OTP gen_server回调模块必须提供handle_cast函数?

时间:2018-05-04 11:07:56

标签: erlang otp gen-server

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

但我不明白为什么handle_cast是必需的。无法gen_server模块提供默认实现,就像许多其他回调一样?它可能是一个noop喜欢

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

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

1 个答案:

答案 0 :(得分:1)

  

和handle_call是创建服务器进程的主要目的:为请求提供服务。

客户端 - 服务器架构可以应用于更广泛的问题,而不仅仅是提供文档的Web服务器。一个例子是几本erlang书中讨论的频率服务器。客户端可以从服务器请求频率进行电话呼叫,然后客户端必须等待服务器在可以进行呼叫之前返回特定频率。这是一种典型的gen_server:call()情况:客户端必须等待服务器返回频率,然后客户端才能拨打电话。

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

现在,"主要目的是什么"频率服务器?分配或取消分配频率?如果服务器没有解除分配频率,那么在一定数量的客户端请求之后,将无法再发送任何频率,客户端将收到一条消息,其中显示“没有可用的频率”#34 ;。因此,为了使系统正常工作,解除频率的行为是至关重要的。换句话说,handle_call()不是"主要目的"服务器 - handle_cast()同样重要 - 需要两个处理程序来保持系统尽可能高效地运行。

  

无法生成gen_server模块,就像它一样提供默认实现   对许多其他回调做了什么?

为什么不能创建gen_server模板,其默认实现为handle_cast()?这是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
%%%===================================================================