常规操作:
try
poolboy:transaction(Pool, fun(Worker) ->
erlang:apply(Module, Function, [Worker | ArgRest])
end, infinity)
catch Class:Reason when Retries =< 0 ->
erlang:raise(Class, Reason, erlang:get_stacktrace());
Class:Reason ->
lager:warning("~p retries for: Class = ~p, Reason = ~p~nMFA = ~p"
, [?MODULE, Class, Reason, {Module, Function, Arguments}]),
timer:sleep(Cooldown),
wrap_retries(Module, Function, Arguments, Retries - 1, Cooldown)
end.
transaction中是一套完整的线程池分配流程,源代码如下:
-spec transaction(Pool :: pool(), Fun :: fun((Worker :: pid()) -> any()),
Timeout :: timeout()) -> any().
transaction(Pool, Fun, Timeout) ->
Worker = poolboy:checkout(Pool, true, Timeout),
try
Fun(Worker)
after
ok = poolboy:checkin(Pool, Worker)
end.
通过checkout取出pool内的线程,如果当前无线程分配,则进入等待
进入checkout函数内部
-spec checkout(Pool :: pool(), Block :: boolean(), Timeout :: timeout())
-> pid() | full.
checkout(Pool, Block, Timeout) ->
CRef = make_ref(),
try
gen_server:call(Pool, {checkout, CRef, Block}, Timeout)
catch
Class:Reason ->
gen_server:cast(Pool, {cancel_waiting, CRef}),
erlang:raise(Class, Reason, erlang:get_stacktrace())
end.
在checkout中,先获取了一个唯一引用,同步调用线程池中的checkout方法
handle_call({checkout, CRef, Block}, {FromPid, _} = From, State) ->
#state{supervisor = Sup,
workers = Workers,
monitors = Monitors,
overflow = Overflow,
max_overflow = MaxOverflow} = State,
case Workers of
[Pid | Left] ->
MRef = erlang:monitor(process, FromPid),
true = ets:insert(Monitors, {Pid, CRef, MRef}),
{reply, Pid, State#state{workers = Left}};
[] when MaxOverflow > 0, Overflow < MaxOverflow ->
{Pid, MRef} = new_worker(Sup, FromPid),
true = ets:insert(Monitors, {Pid, CRef, MRef}),
{reply, Pid, State#state{overflow = Overflow + 1}};
[] when Block =:= false ->
{reply, full, State};
[] ->
MRef = erlang:monitor(process, FromPid),
Waiting = queue:in({From, CRef, MRef}, State#state.waiting),
{noreply, State#state{waiting = Waiting}}
end;
存在多种情况,
1.已有worker,那么重新写入监督关系
2.无worker,但是进程池未溢出,那么建立新worker,将新的监督关系写入ets表
3.无worker,block置为false,表示不等待的情况,那么返回进程池已满
4.无worker,block置为true,那么请求进入队列,等待进程池空闲
checkout之后,返回一个空闲的worker进程,通过fun函数执行相关操作
执行完成后,通过checkin操作将worker进程返回给进程池
handle_cast({checkin, Pid}, State = #state{monitors = Monitors}) ->
case ets:lookup(Monitors, Pid) of
[{Pid, _, MRef}] ->
true = erlang:demonitor(MRef),
true = ets:delete(Monitors, Pid),
NewState = handle_checkin(Pid, State),
{noreply, NewState};
[] ->
{noreply, State}
end;
在checkin操作中,解除了监督关系erlang:demonitor(MRef),删除了ets表中的监督关联
最后执行handle_checkin函数检测队列中是否存在等待队列
handle_checkin(Pid, State) ->
#state{supervisor = Sup,
waiting = Waiting,
monitors = Monitors,
overflow = Overflow,
strategy = Strategy} = State,
case queue:out(Waiting) of
{{value, {From, CRef, MRef}}, Left} ->
true = ets:insert(Monitors, {Pid, CRef, MRef}),
gen_server:reply(From, Pid),
State#state{waiting = Left};
{empty, Empty} when Overflow > 0 ->
ok = dismiss_worker(Sup, Pid),
State#state{waiting = Empty, overflow = Overflow - 1};
{empty, Empty} ->
Workers = case Strategy of
lifo -> [Pid | State#state.workers];
fifo -> State#state.workers ++ [Pid]
end,
State#state{workers = Workers, waiting = Empty, overflow = 0}
end.
若存在等待队列,则重新建立新的监督关系。否则,更新进程池状态,等待状态置空。
以上,进程池分配操作完成。