erlang学习笔记之gen_server的模板填写与简单测试

名为my_bank的银行模块

-behaviour(gen_server).
-export([start/0]).
%% gen_server回调函数
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).

-compile(export_all).
-define(SERVER, ?MODULE).

start() -> gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
stop() -> gen_server:call(?MODULE, stop).

new_account(Who) -> gen_server:call(?MODULE, {new, Who}).
deposit(Who, Amount) -> gen_server:call(?MODULE, {add, Who, Amount}).
withdraw(Who, Amount) -> gen_server:call(?MODULE, {remove, Who, Amount}).

init([]) -> {ok, ets:new(?MODULE, [])}.

handle_call({new, Who}, _From, Tab) ->
	Reply = case ets:lookup(Tab, Who) of
						[] -> ets:insert(Tab, {Who, 0}),
							{welcome, Who};
						[_] -> {Who, you_already_are_a_customer}
					end,
	{reply, Reply, Tab};

handle_call({add, Who, X}, _From, Tab) ->
	Reply = case ets:lookup(Tab, Who) of
						[] -> not_a_customer;
						[{Who, Balance}] ->
							NewBalance = Balance + X,
							ets:insert(Tab, {Who, NewBalance}),
							{ok, Who, your_balance_is, NewBalance}
					end,
	{reply, Reply, Tab};

handle_call({remove, Who, X}, _From, Tab) ->
	Reply = case ets:lookup(Tab, Who) of
						[] -> not_a_customer;
						[{Who, Balance}] when X =< Balance ->
							NewBalance = Balance - X,
							ets:insert(Tab, {Who, NewBalance}),
							{thanks, Who, your_balance_is, NewBalance};
						[{Who, Balance}] ->
							{none, Who, you_only_have, Balance, in_the_bank}
					end,
	{reply, Reply, Tab};

handle_call(stop, _From, Tab) ->
	{stop, normal, stopped, Tab}.
handle_cast(_Msg, State) -> {noreply, State}.
handle_info(_Info, State) -> {noreply, State}.
terminate(_Reason, _State) -> ok.
code_change(_OldVsn, State, _Extra) -> {ok, State}.

从这个简单的模板,我们能够清楚的看到代码的整体结构

只介绍了最简单的gen_server使用方式,但它应该足以应付大多数需求了。

复杂程度更高的应用程序经常会让gen_server回复一个noreply返回值,并把真正的回复任务委派给另一个进程。

把服务器行为抽象成两个部分:通用部分可以用于所有服务器,特有部分(处理模块)可以用来对通用部分进行定制。

这么做的优点是代码能整齐地一分为二。通用部分解决众多并发和错误处理问题,而处理模块只包含顺序代码。 

练习

任务中心(job_center)持有一个必须完成的任务队列,这些任务会被编号,任何人都能向队列添加任务。工人可以从队列请求任务,并告诉任务中心已经执行了某项任务。任务是由fun表示的,要执行任务F,工人必须执行F()函数。 (1) 实现任务中心的基本功能,它的接口如下。

1.job_centre:start_link() -> true 启动任务中心服务器。

2. job_centre:add_job(F) -> JobNumber 添加任务F到任务队列,然后返回一个整数任务编号。

3.job_centre:work_wanted() -> {JobNumber,F} | no 请求任务。如果工人想要一个任务,就调用job_centre:work_wanted()。如果队列里 有任务,就会返回一个{JobNumber, F}元组。工人执行F()来完成任务。如果队列里没 有任务,则会返回no。请确保同一项任务每次只分配给一个工人,并确保系统是公平的, 意思是任务按照请求的顺序进行分配。 

4.job_centre:job_done(JobNumber) 发出任务完成的信号。如果工人完成了某一项任务,就必须调用job_centre:job_done (JobNumber)。

5. 添加一个名为job_centre:statistics()的统计函数,让它报告队列内、进行中和已完 成任务的状态。

-behaviour(gen_server).
-export([test/0, start_link/0, stop/0, init/1, add_job/1, work_wanted/0, job_done/1, handle_call/3, handle_cast/2, handle_info/2, job_statistic/0]).

test() ->
	start_link(),
	add_job(fun() -> good_job end).

%% 启动
start_link() ->
	gen_server:start_link({global, ?MODULE}, ?MODULE, [], []).
%% 停止
stop() ->
	gen_server:call(?MODULE, stop).

%% 添加任务F到任务队列
add_job(F) ->
	gen_server:call(?MODULE, {add_job,F}).

%% 创建第一个键,JobNumber为0,因为它并不是任务
init([]) ->
	State = ets:new(?MODULE, []),
	ets:insert(State, {job_index, 0}),
	{ok, State}.

%% 请求任务
work_wanted() ->
	gen_server:call(?MODULE, work_wanted).

%% 完成任务
job_done(JobNumber) ->
	gen_server:call(?MODULE, {job_done, JobNumber}).

%% 查询当前任务中心所有任务状态
job_statistic() ->
	gen_server:call(?MODULE, job_statistic).
.

%% 增加,JobNumber+1,状态为等待
handle_call({add_job, Fun}, _From, State) ->
	[{job_index, JobNumber}] = ets:lookup(State, job_index),
	ets:insert(State, [{job_index, JobNumber + 1}, {JobNumber, Fun, wait}]),
	{reply, JobNumber, State};

%% 查询当前任务中心所有任务状态
handle_call(job_statistic, _From, State) ->
	TaskWait = ets:match(State, {'$1', '$2', wait}),
	TaskDoing = ets:match(State, {'$1', '$2', doing}),
	TaskDone = ets:match(State, {'$1', '$2', done}),
	{reply, {length(TaskWait), length(TaskDoing), length(TaskDone)}, State};


handle_call(work_wanted, {_Pid, _Ref}, State) ->
	case ets:match(State, {'$1', '$2', wait}, 1) of
		'$end_of_table' ->
			{reply, no, State};
		{[[JobNumber, Fun]], _} ->
			ets:update_element(State, JobNumber, {3, doing}),
			{reply, {JobNumber, Fun}, State}
	end;


handle_call({job_done, JobNumber, Status}, _From, State) ->
			case ets:lookup_element(State, JobNumber, 3) of
				done ->
					Reply = done;
				doing ->
					Reply = ok,
					ets:update_element(State, JobNumber, {3, Status});
				wait ->
					Reply = err
			end,
			{reply, Reply, State};

handle_call(stop, _From, State) ->
	{stop, normal, stop, State}.

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

handle_info(_Info, State) ->
	{noreply, State}.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值