为什么Erlang/OTPgen_server回调模块必须提供handle_cast功能?
Why Erlang/OTP gen_server callback module must provide handle_cast function?
我能理解为什么回调模块必须提供init
和handle_call
功能。 init
用于创建初始状态,handle_call
是创建服务器进程的主要目的:为请求提供服务。
但我不明白为什么需要 handle_cast
。 gen_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
%%%===================================================================
我能理解为什么回调模块必须提供init
和handle_call
功能。 init
用于创建初始状态,handle_call
是创建服务器进程的主要目的:为请求提供服务。
但我不明白为什么需要 handle_cast
。 gen_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
%%%===================================================================