erlang的实现一个简单的进程池。

erlang进程是非常轻量级的,这个进程池的主要目的是用一种通用的方式去管理和限制系统中运行的资源占用。当运行的工作者进程数量达到上限,进程池还可以把任务放到队列中,只要进程资源被释放,排队的任务就能获得运行,否则任务只能阻塞。

这是进程池的监督树

ppool_supersup监督着所有的进程池。一个进程池由ppool_sup、ppool_serv和worker_sup监督的工作者进程池组成。ppool_serv提供对外的进程池调用api,ppool_sup负责监督单个进程池。

下面是实现代码。

  1. %% ppool_supersup
  2. -module(ppool_supersup).
  3. -behavior(supervisor).
  4. -export([start_link/0, stop/0, start_pool/3, stop_pool/1]).
  5. -export([init/1]).
  6. start_link() ->
  7. supervisor:start_link({local, ppool}, ?MODULE, []).
  8. stop() ->
  9. case whereis(ppool) of
  10. P when is_pid(P) ->
  11. exit(P, kill);
  12. _ ->
  13. ok
  14. end.
  15. init([]) ->
  16. MaxRestart = 6,
  17. MaxTime = 3600,
  18. {ok, {{one_for_one, MaxRestart, MaxTime}, []}}.
  19. start_pool(Name, Limit, MFA) ->
  20. %%每个进程池的最大终止时间设置为10500,这个值并没有什么特殊意义,只是保证足够大,所有子进程都有足够的时间终止。如果实在不知道设置为多大,可以试试infinity
  21. ChildSpec = {Name,
  22. {ppool_sup, start_link, [Name, Limit, MFA]},
  23. permanent, 10500, supervisor, [ppool_sup]},
  24. supervisor:start_child(ppool, ChildSpec).
  25. stop_pool(Name) ->
  26. supervisor:terminate_child(ppool, Name),
  27. supervisor:delete_child(ppool, Name).
  1. %% ppool_sup
  2. -module(ppool_sup).
  3. -export([start_link/3, init/1]).
  4. -behavior(supervisor).
  5. start_link(Name, Limit, MFA) ->
  6. supervisor:start_link(?MODULE, {Name, Limit, MFA}).
  7. init({Name, Limit, MFA}) ->
  8. MaxRestart = 1,
  9. MaxTime = 3600,
  10. {ok, {{one_for_all, MaxRestart, MaxTime},
  11. [{serv,
  12. {ppool_serv, start_link, [Name, Limit, self(), MFA]},
  13. permanent,
  14. 5000,
  15. worker,
  16. [ppool_serv]}]}}.
  1. %% ppool_worker_sup
  2. -module(ppool_worker_sup).
  3. -export([start_link/1, init/1]).
  4. -behavior(supervisor).
  5. start_link(MFA = {_, _, _}) ->
  6. supervisor:start_link(?MODULE, MFA).
  7. init({M, F, A}) ->
  8. MaxRestart = 5,
  9. MaxTime = 3600,
  10. {ok, {{simple_one_for_one, MaxRestart, MaxTime},
  11. [{ppool_worker,
  12. {M, F, A},
  13. 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提供了三种添加任务的方式:

  • 如果进程池中有空间,立刻运行;如果已满,给出无法运行的指示。
  • 如果进程池中有空间,立刻运行;如果已满,调用者进程阻塞等待,任务入队列。
  • 如果进程池中有空间,立刻运行;如果已满,任务入队列,调用者进程不阻塞。
  1. %% ppool_serv
  2. -module(ppool_serv).
  3. -behavior(gen_server).
  4. -export([start/4, start_link/4, run/2, sync_queue/2, async_queue/2, stop/1]).
  5. -export([init/1, handle_call/3, handle_cast/2, handle_info/2, code_change/3, terminate/2]).
  6. -define(SPEC(MFA),
  7. {worker_sup,
  8. {ppool_worker_sup, start_link, [MFA]},
  9. permanent,
  10. 10000,
  11. supervisor,
  12. [ppool_woker_sup]}).
  13. -record(state, {limit = 0,
  14. sup,
  15. refs,
  16. queue = queue:new()}).
  17. start(Name, Limit, Sup, MFA) when is_atom(Name), is_integer(Limit) ->
  18. gen_server:start({local, Name}, ?MODULE, {Limit, MFA, Sup}, []).
  19. start_link(Name, Limit, Sup, MFA) when is_atom(Name), is_integer(Limit) ->
  20. gen_server:start_link({local, Name}, ?MODULE, {Limit, MFA, Sup}, []).
  21. run(Name, Args) ->
  22. gen_server:call(Name, {run, Args}).
  23. sync_queue(Name, Args) ->
  24. gen_server:call(Name, {sync, Args}, infinity).
  25. async_queue(Name, Args) ->
  26. gen_server:cast(Name, {async, Args}).
  27. stop(Name) ->
  28. gen_server:call(Name, stop).
  29. init({Limit, MFA, Sup}) ->
  30. self() ! {start_worker_supervisor, Sup, MFA},
  31. {ok, #state{limit = Limit, refs = gb_sets:empty()}}.
  32. handle_info({'DOWN', Ref, process, _Pid, _}, S = #state{refs = Refs}) ->
  33. case gb_sets:is_element(Ref, Refs) of
  34. true ->
  35. handle_down_worker(Ref, S);
  36. false ->
  37. {noreply, S}
  38. end;
  39. handle_info({start_worker_supervisor, Sup, MFA}, S = #state{}) ->
  40. {ok, Pid} = supervisor:start_child(Sup, ?SPEC(MFA)),
  41. link(Pid),
  42. {noreply, S#state{sup = Pid}};
  43. handle_info(_Msg, State) ->
  44. {noreply, State}.
  45. handle_call({run, Args}, _From, S = #state{limit = N, sup = Sup, refs = R}) when N > 0 ->
  46. {ok, Pid} = supervisor:start_child(Sup, Args),
  47. Ref = erlang:monitor(process, Pid),
  48. {reply, {ok, Pid}, S#state{limit = N - 1, refs = gb_sets:add(Ref, R)}};
  49. handle_call({run, _Args}, _From, S = #state{limit = N}) when N =< 0 ->
  50. {reply, noalloc, S};
  51. handle_call({sync, Args}, _From, S = #state{limit=N, sup=Sup, refs=R}) when N > 0 ->
  52. {ok, Pid} = supervisor:start_child(Sup, Args),
  53. Ref = erlang:monitor(process, Pid),
  54. {reply, {ok,Pid}, S#state{limit=N-1, refs=gb_sets:add(Ref,R)}};
  55. handle_call({sync, Args}, From, S = #state{queue=Q}) ->
  56. {noreply, S#state{queue=queue:in({From, Args}, Q)}};
  57. handle_call(stop, _From, State) ->
  58. {stop, normal, ok, State};
  59. handle_call(_Msg, _From, State) ->
  60. {noreply, State}.
  61. handle_cast({async, Args}, S=#state{limit=N, sup=Sup, refs=R}) when N > 0 ->
  62. {ok, Pid} = supervisor:start_child(Sup, Args),
  63. Ref = erlang:monitor(process, Pid),
  64. {noreply, S#state{limit=N-1, refs=gb_sets:add(Ref,R)}};
  65. handle_cast({async, Args}, S=#state{limit=N, queue=Q}) when N =< 0 ->
  66. {noreply, S#state{queue=queue:in(Args,Q)}};
  67. handle_cast(_Msg, State) ->
  68. {noreply, State}.
  69. code_change(_OldVsn, State, _Extra) ->
  70. {ok, State}.
  71. terminate(_Reason, _State) ->
  72. ok.
  73. handle_down_worker(Ref, S = #state{limit = L, sup = Sup, refs = Refs}) ->
  74. case queue:out(S#state.queue) of
  75. {{value, {From, Args}}, Q} ->
  76. {ok, Pid} = supervisor:start_child(Sup, Args),
  77. NewRef = erlang:monitor(process, Pid),
  78. NewRefs = gb_sets:insert(NewRef, gb_sets:delete(Ref, Refs)),
  79. gen_server:reply(From, {ok, Pid}),
  80. {noreply, S#state{refs = NewRefs, queue = Q}};
  81. {{value, Args}, Q} ->
  82. {ok, Pid} = supervisor:start_child(Sup, Args),
  83. NewRef = erlang:monitor(process, Pid),
  84. NewRefs = gb_sets:insert(NewRef, gb_sets:delete(Ref, Refs)),
  85. {noreply, S#state{refs = NewRefs, queue = Q}};
  86. {empty, _} ->
  87. {noreply, S#state{limit = L + 1, refs = gb_sets:delete(Ref, Refs)}}
  88. end.

摘自《learn you some Erlang for great good》

  

erlang实现一个进程池 pool的更多相关文章

  1. [转]Python多进程并发操作中进程池Pool的应用

    Pool类 在使用Python进行系统管理时,特别是同时操作多个文件目录或者远程控制多台主机,并行操作可以节约大量的时间.如果操作的对象数目不大时,还可以直接使用Process类动态的生成多个进程,十 ...

  2. Python多进程并发操作中进程池Pool的应用

    Pool类 在使用Python进行系统管理时,特别是同时操作多个文件目录或者远程控制多台主机,并行操作可以节约大量的时间.如果操作的对象数目不大时,还可以直接使用Process类动态的生成多个进程,十 ...

  3. python 使用进程池Pool进行并发编程

    进程池Pool 当需要创建的子进程数量不多时,可以直接利用multiprocessing中的Process动态成生多个进程,但如果是上百甚至上千个目标,手动的去创建进程的工作量巨大,此时就可以用到mu ...

  4. Python多进程库multiprocessing中进程池Pool类的使用

    问题起因 最近要将一个文本分割成好几个topic,每个topic设计一个regressor,各regressor是相互独立的,最后汇总所有topic的regressor得到总得预测结果.没错!类似ba ...

  5. python 全栈开发,Day40(进程间通信(队列和管道),进程间的数据共享Manager,进程池Pool)

    昨日内容回顾 进程 multiprocess Process —— 进程 在python中创建一个进程的模块 start daemon 守护进程 join 等待子进程执行结束 锁 Lock acqui ...

  6. python 进程池pool简单使用

    平常会经常用到多进程,可以用进程池pool来进行自动控制进程,下面介绍一下pool的简单使用. 需要主动是,在Windows上要想使用进程模块,就必须把有关进程的代码写if __name__ == ‘ ...

  7. Python多进程库multiprocessing中进程池Pool类的使用[转]

    from:http://blog.csdn.net/jinping_shi/article/details/52433867 Python多进程库multiprocessing中进程池Pool类的使用 ...

  8. python 进程池pool

    进程池子 当你成千上万的业务需要创建成千上万的进程时,我们可以提前定义一个进程池 from multiprocessing import Pool p = Pool(10) #进程池创建方式,类似空任 ...

  9. 进程池----Pool(老的方式)----回调

    之后的进程池使用的是 ProcessPoolExecutor,它的底层使用的就是pool 为什么要有进程池?进程池的概念. 在程序实际处理问题过程中,忙时会有成千上万的任务需要被执行,闲时可能只有零星 ...

随机推荐

  1. Android Developer -- Bluetooth篇 概述

    Bluetooth 安卓平台支持蓝牙网络协议栈,它允许设备与其他蓝牙设备进行无线交换数据.应用程序框架通过安卓蓝牙APIs提供访问蓝牙功能.这些APIs使应用程序通过无线连接到其他蓝牙设备,使点对点和 ...

  2. linux之rpm指令

    rmp原本是Red Hat Linux发行版专门用来管理Linux各项套件的程序,由于它遵循GPL规则且功能强大方便,因而广受欢迎.逐渐受到其他发行版的采用.RPM套件管理方式的出现,让Linux易于 ...

  3. IdHTTPServer(indy10)开发REST中间件

    IdHTTPServer(indy10)开发REST中间件 浏览器通过“get”方式查询数据URL样例:http://127.0.0.1:7777/query?sql=select * from t1 ...

  4. 泳池水面fresnel 的近似替代

    vs float4 ep = TBMultiply(ModelViewMatrix, FinalPosition); DistFromEye.x = TBSaturate( 10.0 + ep.z / ...

  5. 对Linux文件权限的理解

    755,775,777,ugoa 等分别代表什么含义?这些数字是如何得到的? 1.常用的linux文件权限: 444 -r--r--r-- 600 -rw------- 644 -rw-r--r-- ...

  6. 前端存储之indexedDB

    在前一个阶段的工作中,项目组要开发一个平台,为了做出更好的用户体验,实现快速.高质量的交互,从而更快得到用户的反馈,要求在前端把数据存储起来,之后我去研究了下现在比较流行的前端存储数据库,找到了ind ...

  7. DevExpress控件之GridControl、GridView

    GridControl对应标准WinForm里的GridView,相当于是一个控件,里面包含多个GridView也可以放其它的控件 禁止修改gridView1.OptionsBehavior.Edit ...

  8. 阻止右键菜单(阻止默认事件)&&跟随鼠标移动(大图展示)&&自定义右键菜单

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  9. Linux下xargs命令详解及xargs与管道的区别

    在工作中经常会接触到xargs命令,特别是在别人写的脚本里面也经常会遇到,但是却很容易与管道搞混淆,本篇会详细讲解到底什么是xargs命令,为什么要用xargs命令以及与管道的区别.为什么要用xarg ...

  10. How to Handle Exception