制作一个 任务中心
-module(job_centre).
-behaviour(gen_server).
-export([start_link/0, add_job/1, work_wanted/0, job_done/1, statistics/0]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-define(SERVER, ?MODULE).
-record(state, {queue = queue:new(), in_progress = [], completed = [], workers = [], id = 0}).
start_link() -> gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
add_job(F) -> gen_server:call(?SERVER, {add_job, F}).
work_wanted() -> gen_server:call(?SERVER, work_wanted).
job_done(JobNumber) -> gen_server:cast(?SERVER, {job_done, JobNumber}).
statistics() -> gen_server:call(?SERVER, statistics).
init([]) -> {ok, #state{}}.
handle_call({add_job, F}, _From, State) ->
Id = State#state.id + 1,
Queue = queue:in({Id, F}, State#state.queue),
{reply, Id, State#state{queue= Queue, id = Id}};
handle_call(work_wanted, _From, State) ->
case queue:out(State#state.queue) of
{empty, _} ->
{reply, no, State};
{{value, {JobNumber, F}}, NewQueue} ->
% 启动监控
WorkerPid = self(),
erlang:monitor(process, WorkerPid),
InProgress = [JobNumber | State#state.in_progress],
Workers = [{JobNumber, WorkerPid, F} | State#state.workers],
{reply, {JobNumber, F}, State#state{queue = NewQueue, in_progress = InProgress, workers = Workers}}
end;
handle_call(statistics, _From, State) ->
{reply, {queue:len(State#state.queue), length(State#state.in_progress), length(State#state.completed)}, State}.
handle_cast({job_done, Id}, State) ->
io:format("Job ~p is done~n", [Id]),
Completed = [Id | State#state.completed],
InProgress = lists:delete(Id, State#state.in_progress),
{noreply, State#state{in_progress = InProgress, completed = Completed}}.
handle_info({'DOWN', _, process, WorkerPid, _Reason}, State) ->
{Id, _WorkerPid, F} = lists:keyfind(WorkerPid, 2, State#state.workers),
Queue = queue:in({Id, F}, State#state.queue),
InProgress = lists:delete(Id, State#state.in_progress),
{noreply, State#state{queue = Queue, in_progress = InProgress, workers = lists:keydelete(WorkerPid, 2, State#state.workers)}};
handle_info(_Info, State) -> {noreply, State}.
terminate(_Reason, _State) -> ok.
code_change(_OldVsn, State, _Extra) -> {ok, State}.
-module(job_centre_test).
-include_lib("eunit/include/eunit.hrl").
-export([test/0]).
start_job_centre() ->
job_centre:start_link(),
ok.
stop_job_centre() ->
gen_server:call(?MODULE, stop),
ok.
simulate_worker_crash() ->
receive
{'DOWN', _, process, _, _Reason} ->
io:format("工作进程挂掉~n")
after 0 ->
io:format("超时~n")
end.
test() ->
%% 在erlang中使用单元测试判断函数值是否符合预期
?assertEqual(ok, start_job_centre()),
F = fun() -> io:format("Task 1") end,
?assertEqual(1, job_centre:add_job(F)),
?assertEqual({1, F}, job_centre:work_wanted()),
simulate_worker_crash(),
?assertEqual({1, F}, job_centre:work_wanted()),
stop_job_centre().