Erlang实现进程池
开发工作中,经常会碰到进程池或者线程池,或者其它的资源池。在这里,用erlang实现一个简单的进程池。
erlang进程是非常轻量级的,这个进程池的主要目的是用一种通用的方式去管理和限制系统中运行的资源占用。当运行的工作者进程数量达到上限,进程池还可以把任务放到队列中,只要进程资源被释放,排队的任务就能获得运行,否则任务只能阻塞。
这是进程池的监督树:
ppool_supersup监督着所有的进程池。一个进程池由ppool_sup、ppool_serv和worker_sup监督的工作者进程池组成。ppool_serv提供对外的进程池调用api,ppool_sup负责监督单个进程池。
下面是实现代码。
%% ppool_supersup
-module(ppool_supersup).
-behavior(supervisor).
-export([start_link/0, stop/0, start_pool/3, stop_pool/1]).
-export([init/1]).
start_link() ->
supervisor:start_link({local, ppool}, ?MODULE, []).
stop() ->
case whereis(ppool) of
P when is_pid(P) ->
exit(P, kill);
_ ->
ok
end.
init([]) ->
MaxRestart = 6,
MaxTime = 3600,
{ok, {{one_for_one, MaxRestart, MaxTime}, []}}.
start_pool(Name, Limit, MFA) ->
%%每个进程池的最大终止时间设置为10500,这个值并没有什么特殊意义,只是保证足够大,所有子进程都有足够的时间终止。如果实在不知道设置为多大,可以试试infinity。
ChildSpec = {Name,
{ppool_sup, start_link, [Name, Limit, MFA]},
permanent, 10500, supervisor, [ppool_sup]},
supervisor:start_child(ppool, ChildSpec).
stop_pool(Name) ->
supervisor:terminate_child(ppool, Name),
supervisor:delete_child(ppool, Name).
%% ppool_sup
-module(ppool_sup).
-export([start_link/3, init/1]).
-behavior(supervisor).
start_link(Name, Limit, MFA) ->
supervisor:start_link(?MODULE, {Name, Limit, MFA}).
init({Name, Limit, MFA}) ->
MaxRestart = 1,
MaxTime = 3600,
{ok, {{one_for_all, MaxRestart, MaxTime},
[{serv,
{ppool_serv, start_link, [Name, Limit, self(), MFA]},
permanent,
5000,
worker,
[ppool_serv]}]}}.
%% ppool_worker_sup
-module(ppool_worker_sup).
-export([start_link/1, init/1]).
-behavior(supervisor).
start_link(MFA = {_, _, _}) ->
supervisor:start_link(?MODULE, MFA).
init({M, F, A}) ->
MaxRestart = 5,
MaxTime = 3600,
{ok, {{simple_one_for_one, MaxRestart, MaxTime},
[{ppool_worker,
{M, F, A},
temporary, 5000, worker, [M]}]}}.
ppool_serv是最复杂的一个模块了。因为ppool_serv对外提供接口,它需要能联系到worker_sup。如果由ppool_sup同时启动ppool_serv和worker_sup,存在乱序的风险,除非都注册进程名。但erlang中对于原子的使用一定要慎重,能少用就少用。所以在这儿,由ppool_serv动态添加worker_sup到ppool_sup。
ppool_serv提供了三种添加任务的方式:
- 如果进程池中有空间,立刻运行;如果已满,给出无法运行的指示。
- 如果进程池中有空间,立刻运行;如果已满,调用者进程阻塞等待,任务入队列。
- 如果进程池中有空间,立刻运行;如果已满,任务入队列,调用者进程不阻塞。
%% ppool_serv
-module(ppool_serv).
-behavior(gen_server).
-export([start/4, start_link/4, run/2, sync_queue/2, async_queue/2, stop/1]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, code_change/3, terminate/2]).
-define(SPEC(MFA),
{worker_sup,
{ppool_worker_sup, start_link, [MFA]},
permanent,
10000,
supervisor,
[ppool_woker_sup]}).
-record(state, {limit = 0,
sup,
refs,
queue = queue:new()}).
start(Name, Limit, Sup, MFA) when is_atom(Name), is_integer(Limit) ->
gen_server:start({local, Name}, ?MODULE, {Limit, MFA, Sup}, []).
start_link(Name, Limit, Sup, MFA) when is_atom(Name), is_integer(Limit) ->
gen_server:start_link({local, Name}, ?MODULE, {Limit, MFA, Sup}, []).
run(Name, Args) ->
gen_server:call(Name, {run, Args}).
sync_queue(Name, Args) ->
gen_server:call(Name, {sync, Args}, infinity).
async_queue(Name, Args) ->
gen_server:cast(Name, {async, Args}).
stop(Name) ->
gen_server:call(Name, stop).
init({Limit, MFA, Sup}) ->
self() ! {start_worker_supervisor, Sup, MFA},
{ok, #state{limit = Limit, refs = gb_sets:empty()}}.
handle_info({'DOWN', Ref, process, _Pid, _}, S = #state{refs = Refs}) ->
case gb_sets:is_element(Ref, Refs) of
true ->
handle_down_worker(Ref, S);
false ->
{noreply, S}
end;
handle_info({start_worker_supervisor, Sup, MFA}, S = #state{}) ->
{ok, Pid} = supervisor:start_child(Sup, ?SPEC(MFA)),
link(Pid),
{noreply, S#state{sup = Pid}};
handle_info(_Msg, State) ->
{noreply, State}.
handle_call({run, Args}, _From, S = #state{limit = N, sup = Sup, refs = R}) when N > 0 ->
{ok, Pid} = supervisor:start_child(Sup, Args),
Ref = erlang:monitor(process, Pid),
{reply, {ok, Pid}, S#state{limit = N - 1, refs = gb_sets:add(Ref, R)}};
handle_call({run, _Args}, _From, S = #state{limit = N}) when N =< 0 ->
{reply, noalloc, S};
handle_call({sync, Args}, _From, S = #state{limit=N, sup=Sup, refs=R}) when N > 0 ->
{ok, Pid} = supervisor:start_child(Sup, Args),
Ref = erlang:monitor(process, Pid),
{reply, {ok,Pid}, S#state{limit=N-1, refs=gb_sets:add(Ref,R)}};
handle_call({sync, Args}, From, S = #state{queue=Q}) ->
{noreply, S#state{queue=queue:in({From, Args}, Q)}};
handle_call(stop, _From, State) ->
{stop, normal, ok, State};
handle_call(_Msg, _From, State) ->
{noreply, State}.
handle_cast({async, Args}, S=#state{limit=N, sup=Sup, refs=R}) when N > 0 ->
{ok, Pid} = supervisor:start_child(Sup, Args),
Ref = erlang:monitor(process, Pid),
{noreply, S#state{limit=N-1, refs=gb_sets:add(Ref,R)}};
handle_cast({async, Args}, S=#state{limit=N, queue=Q}) when N =< 0 ->
{noreply, S#state{queue=queue:in(Args,Q)}};
handle_cast(_Msg, State) ->
{noreply, State}.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
terminate(_Reason, _State) ->
ok.
handle_down_worker(Ref, S = #state{limit = L, sup = Sup, refs = Refs}) ->
case queue:out(S#state.queue) of
{{value, {From, Args}}, Q} ->
{ok, Pid} = supervisor:start_child(Sup, Args),
NewRef = erlang:monitor(process, Pid),
NewRefs = gb_sets:insert(NewRef, gb_sets:delete(Ref, Refs)),
gen_server:reply(From, {ok, Pid}),
{noreply, S#state{refs = NewRefs, queue = Q}};
{{value, Args}, Q} ->
{ok, Pid} = supervisor:start_child(Sup, Args),
NewRef = erlang:monitor(process, Pid),
NewRefs = gb_sets:insert(NewRef, gb_sets:delete(Ref, Refs)),
{noreply, S#state{refs = NewRefs, queue = Q}};
{empty, _} ->
{noreply, S#state{limit = L + 1, refs = gb_sets:delete(Ref, Refs)}}
end.
摘自《learn you some Erlang for great good》,最近出了中文版本,人民邮电出版社的《Erlang趣学指南》
Erlang实现进程池的更多相关文章
- erlang的进程池。
转自: http://blog.sina.com.cn/s/blog_96b8a1540101542m.html 主要组成部分: https://github.com/devinus/poolboy ...
- erlang实现一个进程池 pool
erlang的实现一个简单的进程池. erlang进程是非常轻量级的,这个进程池的主要目的是用一种通用的方式去管理和限制系统中运行的资源占用.当运行的工作者进程数量达到上限,进程池还可以把任务放到队列 ...
- python进程池:multiprocessing.pool
本文转至http://www.cnblogs.com/kaituorensheng/p/4465768.html,在其基础上进行了一些小小改动. 在利用Python进行系统管理的时候,特别是同时操作多 ...
- erlang 200w进程测试
参照<programing erlang>书例子,测试erlang创建进程性能 创建N个进程 都wait,然后挨个发送die关闭进程,代码如下: 测试场景:200w进程.MacBook P ...
- 64位进程池HashCode兼容处理
背景 net旧项目使用32位生成的HashCode,存储到数据库中.迁移到64位上,就需要对HashCode做兼容处理. 解决方案 1:进程池配置支持32位程序. 2:对Hashcode做兼容处理,[ ...
- Linux客户/服务器程序设计范式2——并发服务器(进程池)
引言 让服务器在启动阶段调用fork创建一个子进程池,通过子进程来处理客户端请求.子进程与父进程之间使用socketpair进行通信(为了方便使用sendmsg与recvmsg,如果使用匿名管道,则无 ...
- PYTHON多进程编码结束之进程池POOL
结束昨晚开始的测试. 最后一个POOL. A,使用POOL的返回结果 #coding: utf-8 import multiprocessing import time def func(msg): ...
- python(进程池/线程池)
进程池 import multiprocessing import time def do_calculation(data): print(multiprocessing.current_proce ...
- python进程池剖析(三)
之前文章对python中进程池的原理.数据流以及应用从代码角度做了简单的剖析,现在让我们回头看看标准库中对进程池的实现都有哪些值得我们学习的地方.我们知道,进程池内部由多个线程互相协作,向客户端提供可 ...
随机推荐
- 【习题 5-7 UVA - 12100】Printer Queue
[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 用队列和multiset就能完成模拟 [代码] #include <bits/stdc++.h> using names ...
- windows ffmpeg 的安装
本文我们要安装的是 windows 下的 ffmpeg 命令行工具,安装的步骤十分简单,分为:下载.解压.配置环境变量. 下载,进入 http://ffmpeg.org/download.html#b ...
- Spring的资源抽象Resource2实体类
aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAA0UAAAGkCAIAAABxYhnsAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWX ...
- 关于Linux启动时挂载rootfs的几种方式
一直对Linux启动时挂载根文件系统的过程存在着很多疑问,今天在水木精华区找到了有用的资料,摘录如下: 1.Linux启动时,经过一系列初始化之后,需要mount 根文件系统,为最后运行init进程等 ...
- SDL2源码分析1:初始化(SDL_Init())
===================================================== SDL源码分析系列文章列表: SDL2源码分析1:初始化(SDL_Init()) SDL2源 ...
- jquery插件课程2 放大镜、多文件上传和在线编辑器插件如何使用
jquery插件课程2 放大镜.多文件上传和在线编辑器插件如何使用 一.总结 一句话总结:插件使用真的还是比较简单的,引包,初始化,配置参数(json),配置数据(json),而后两步不是必须的.而且 ...
- mycat server.xml 配置文件详解
<?xml version="1.0" encoding="UTF-8"?> <!-- - - Licensed under the Apac ...
- 【死磕Java并发】—–J.U.C之AQS(一篇就够了)
[隐藏目录] 1 独占式 1.1 独占式同步状态获取 1.2 独占式获取响应中断 1.3 独占式超时获取 1.4 独占式同步状态释放 2 共享式 2.1 共享式同步状态获取 2.2 共享式同步状态释放 ...
- Ubuntu snmp配置
http://wenku.baidu.com/link?url=7ieAta_w87NDrTOT_DyEQSj4Rd9i82YRUGQl--g077oC3ftckgH7wpT5QEyir-NtZLA3 ...
- java完美equals方法代码段
public boolean equals(Object otherObject) { if(this == otherObject) { // 检測this与otherObject是否引用同一个对象 ...