基于Erlang实现的一个简单的并发控制程序
来源:互联网 发布:1024邀请码多少钱淘宝 编辑:程序博客网 时间:2024/05/16 00:31
这篇文章是我之前在RYTong内部分享的一篇文章,摘取了有用的部分。当时帮助另一个某项目解决一个并发控制的问题,基于此跟大家分享一个关于Erlang process的小程序。
问题背景
在开始解决之前先了解下具体问题吧。当时该项目上有一个批处理的功能,会在数据库中读取一个数据列表出来,针对每一个列表元素启动一个Erlang process,使用数据向银行后台发送请求。很不幸的,银行后台承受不了这样大的压力,希望我们在批处理时进行并发控制。
了解具体问题之后,我的思路如下:
- 我们需要提供一个API,输入一个任务列表、一个批处理函数和一个最大并发数,实现并发的调用批处理函数处理任务列表的每一个元素,并且同时间存在的Erlang process数量不能超过最大并发数。
- 为了对批处理的process(worker process)数量进行管理,我们需要一个监工monitor。
- 这个monitor需要准确的知道当前worker process的数量,因此worker process的创建需要由monitor完成,并且worker process完成任务后需要通知monitor。
- 在worker process数量达到上限时,程序需要等待,直到monitor为当前的任务创建了新的worker process。
看看代码
-module(concurrency_control).-export ([batch_work/3]).batch_work(WorkList, BatchFunc, WorkersNum) -> {ok, Pid} = start_monitor(WorkersNum, BatchFunc), io:format("monitor started ~p~n", [Pid]), do_batch_work(WorkList, Pid, WorkersNum, BatchFunc).do_batch_work([], Pid, _, _) -> Pid ! {self(), stop}, ok;do_batch_work([H|T] = List, Pid, WorkersNum, BatchFunc) -> case is_process_alive(Pid) of true -> Pid ! {self(), exec, H}, receive {Pid, ok} -> do_batch_work(T, Pid, WorkersNum, BatchFunc) end; false -> batch_work(List, BatchFunc, WorkersNum) end.start_monitor(Num, BatchFunc) -> Parent = self(), Pid = spawn(fun() -> concurrency_monitor(Num, BatchFunc, Parent) end), receive {Pid, started} -> {ok, Pid} end.concurrency_monitor(Num, BatchFunc, Parent) -> process_flag(trap_exit, true), Parent ! {self(), started}, loop(0, Num, BatchFunc, Parent).loop(0, Max, BatchFunc, Parent) -> receive {From, exec, Arg} -> spawn_link(fun() -> catch BatchFunc(Arg) end), From ! {self(), ok}, loop(1, Max, BatchFunc, Parent); {Parent, stop} -> stop end; loop(Max, Max, BatchFunc, Parent) -> receive {'EXIT', _Worker, _Reason} -> loop(Max - 1, Max, BatchFunc, Parent) end; loop(Current, Max, BatchFunc, Parent) -> receive {From, exec, Arg} -> spawn_link(fun() -> catch BatchFunc(Arg) end), From ! {self(), ok}, loop(Current + 1, Max, BatchFunc, Parent); {'EXIT', _Worker, _Reason} -> loop(Current - 1, Max, BatchFunc, Parent) end.
解释如下:
- 在API实现的第一句代码,我便创建了一个monitor process,monitor执行的函数为concurrency_monitor/3。
- 在concurrency_monitor/3中调用了process_flag(trap_exit, true),这个方法会捕获worker process退出的消息(无论正常或异常)。前提条件是worker process与monitor建立了link,因此我在loop函数中使用了spawn_link函数创建worker process。
- monitor的循环体就是loop函数,循环期间monitor会接收三种消息。{From, exec, Arg}消息会创建worker process,并以Arg为参数调用批处理函数,创建完成后向From发送{self(), ok}消息,通知From创建成功。{‘EXIT’, _Worker, _Reason}消息为worker process退出的消息,此时monitor会将当前worker数量减一。{Parent, stop}消息会让monitor退出。
- 创建完monitor后,do_batch_work函数会遍历任务列表,使用每一个元素向monitor发送{self(), exec, H}消息。并接受monitor发送的{Pid, ok}消息。
- 通过47到51行的代码,我们可以知道当worker数量达到最大值时,monitor不会处理{From, exec, Arg}消息,因此此时do_batch_work会在阻塞在18行的receive处。直到一个worker完成工作后,向monitor发送{‘EXIT’, _Worker, _Reason}消息,monitor才会继续处理{From, exec, Arg}消息来创建worker进程。
- 当遍历完任务列表后,程序会向monitor发送{self(), stop}消息,通知monitor退出。大家可能会觉得这样做有问题,因为monitor可能还有没创建的worker,提前退出会使得任务列表没有被全部处理。其实是没关系的,因为Erlang process是顺序处理消息的,因为程序是最后发送{self(), stop}消息的,所以monitor一定会创建完所有worker之后退出的。
- 仔细分析38到46行的代码,monitor不止会在创建完所有worker之后退出,还会等待所有worker完成任务后再处理{self(), stop}消息。
示例
8> L= lists:seq(1, 20).[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]9> Func = fun(Input) -> io:format("~p: execute task with ~p~n", [time(), Input]), timer:sleep(1000) end.10> concurrency_control:batch_work(L, Func, 4).monitor started <0.134.0>{10,40,34}: execute task with 1{10,40,34}: execute task with 2{10,40,34}: execute task with 3{10,40,34}: execute task with 4{10,40,35}: execute task with 5{10,40,35}: execute task with 6{10,40,35}: execute task with 7{10,40,35}: execute task with 8{10,40,36}: execute task with 9{10,40,36}: execute task with 10{10,40,36}: execute task with 11{10,40,36}: execute task with 12{10,40,37}: execute task with 13{10,40,37}: execute task with 14{10,40,37}: execute task with 15{10,40,37}: execute task with 16{10,40,38}: execute task with 17{10,40,38}: execute task with 18{10,40,38}: execute task with 19{10,40,38}: execute task with 20ok
上述示例代码中,批处理函数会打印当前执行的时间并sleep 1秒,同时设置了最大并发数为4。这样我们便会看到每秒只会并发执行4个任务,打印四句日志。
结语
Erlang在编写并发程序时,为我们提供了gen_server、gen_fsm等等设计模式,可以很好的封装并发编程的细节。使用这些模式,也会让代码变得简洁易懂。这个例子中,并没有使用相关模式,为了是让大家熟悉Erlang并发相关的基础,巩固基础之后再使用这些设计模式会起到锦上添花的作用,否则适得其反。
希望这个示例能帮到大家。荆轲刺秦王
0 0
- 基于Erlang实现的一个简单的并发控制程序
- 基于erlang的简单Web服务器实现
- 基于Erlang的并发程序简要
- 基于短信的手机控制程序
- 基于短信的手机控制程序
- 基于短信的手机控制程序
- 基于FPGA的DS18B20控制程序设计及其Verilog实现 (一)
- 基于FPGA的DS18B20控制程序设计及其Verilog实现(三)
- 一个简单的基于select的并发服务器
- 开发一个基于Erlang的网管代理
- erlang 自己搞定一个简单的MakeFile
- 基于httpclient的一个简单刷票器实现
- 基于python实现一个简单的神经网络
- 基于Java实现的一个简单计算器
- 《基于java的远程控制程序》(1)
- 《基于java的远程控制程序》(2)
- 超简单的Erlang复数实现
- erlang 之简单的Diction实现
- java项目debug 的时候总是跳转threadpoolexecutor
- FFmpeg 一些参数的解释
- 白宫决策捕杀拉登现场照片公布
- android中如何判断edittext中数据为空?
- linux kernel的中断子系统之(八):softirq
- 基于Erlang实现的一个简单的并发控制程序
- 安卓触屏进行的图形变换--平移,缩放核心代码
- c++ 函数指针
- 数据显示高考生源持续降 部分高校面临生存挑战
- 中国白领过劳现象调查:商务楼里的黑砖窑童工
- 滚动轴承特征频率计算推导过程
- 30年前一个关于中国、美国教育的预言
- Intel揭幕3D晶体管,新处理器系列产生
- 中科大Linux镜像源正式面向全国用户开张