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中进程池的原理.数据流以及应用从代码角度做了简单的剖析,现在让我们回头看看标准库中对进程池的实现都有哪些值得我们学习的地方.我们知道,进程池内部由多个线程互相协作,向客户端提供可 ...
随机推荐
- 基于PHP实现一个简单的在线聊天功能(轮询ajax )
基于PHP实现一个简单的在线聊天功能(轮询ajax ) 一.总结 1.用的轮询ajax 二.基于PHP实现一个简单的在线聊天功能 一直很想试着做一做这个有意思的功能,感觉复杂的不是数据交互和表结构,麻 ...
- POJ 1384 Piggy-Bank (ZOJ 2014 Piggy-Bank) 完全背包
POJ :http://poj.org/problem?id=1384 ZOJ:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode ...
- 强力推荐微信小程序之简易计算器,很适合小白程序员
原文链接:https://mp.weixin.qq.com/s/gYF7GjTRpeZNoKPAPI9aXA 1 概述 前几日QQ群里的朋友问我有没有计算器小程序案例,今天我们说下小程序计算器,然后就 ...
- 目前以lib后缀的库有两种,一种为静态链接库(Static Libary,以下简称“静态库”),另一种为动态连接库(DLL,以下简称“动态库”)的导入库(Import Libary,以下简称“导入库”)。静态库是一个或者多个obj文件的打包
前以lib后缀的库有两种,一种为静态链接库(Static Libary,以下简称“静态库”),另一种为动态连接库(DLL,以下简称“动态库”)的导入库(Import Libary,以下简称“导入库”) ...
- GDB(十)--调试正在运行的进程
我编写了一个循环: long i; for (i = 0; i < 999999; i++) { mt.a += 1; sleep(1); }把它编译成a ...
- 【66.47%】【codeforces 556B】Case of Fake Numbers
time limit per test2 seconds memory limit per test256 megabytes inputstandard input outputstandard o ...
- 计算git树上随意两点的近期切割点。
1.git是一种分布式代码管理工具,git通过树的形式记录文件的更改历史,比方: base'<--base<--A<--A' ^ | --- B<--B' 小米project师 ...
- ios开发之核心动画四:核心动画-Core Animation--CABasicAnimation基础核心动画
#import "ViewController.h" @interface ViewController () @property (weak, nonatomic) IBOutl ...
- 驱动程序调试方法之printk——printk的原理与直接使用
1.基本原理 (1)在UBOOT里设置console=ttySAC0或者console=tty1 这里是设置控制终端,tySAC0 表示串口, tty1 表示lcd(2)内核用printk打印 内核就 ...
- css强制不换行 多出的字省略号
width: 100%;//需要指定宽度 overflow: hidden;//溢出隐藏 text-overflow: ellipsis; white-space: nowrap;//强制不换行 te ...