Erlang多进程并发小试

本文详细介绍了使用Erlang进行并发计算的方法,从基础概念到实际应用,通过简单的实例展示了Erlang并发计算的特性及其实现方式。包括单进程与多进程计算1到10万的乘积并比较运行时间,揭示了并发计算的优势与限制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  "One of the main reasons for using Erlang instead of other functional languages is Erlang's ability to handle concurrency and distributed programming."  —— 《Erlang User's Guide》

  今天就玩一下Erlang的并发(concurrency)。

  当年我还是写C++的客户端开发,后来因为公司需要被调到服务端开始了Erlang之路。当时函数式编程对于我来说还是非常新鲜的,那奇特的语法让已经习惯OO语法的我感觉到非常别扭,还好部门老大会对新手进行了一些基础的培训,每次培训之后都会留下几个小题目,其中有一题是通过spawn来展示Erlang的并发。我记得我当时是没完成这道题目的,部门老大可能太忙了,也忘记检查,那今天就把当初的作业好好的补一下。

  Erlang的并发,其实就是开多个进程同时干活,这里的进程是Erlang VM里的概念,与操作系统的进程不是同一概念。Erlang的进程是异步(asynchronous)执行的,举个简单例子,spawn 10个进程来分别输出1到10,其执行结果是乱序的,而不是1到10的顺序排列!

  先来试试顺序执行:

1> [io:format("~p ~n", [Index]) || Index <- lists:seq(1, 10)]. 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
[ok,ok,ok,ok,ok,ok,ok,ok,ok,ok]

  结果很明显,就是1到10的顺序输出。

  接下来开10个进程,也是输出1到10,看看结果如何:

2> [spawn(fun() -> io:format("~p ~n", [Index]) end) || Index <- lists:seq(1, 10)].
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 

  咦,看上去结果和顺序执行一模一样,但再多试几次:

8> [spawn(fun() -> io:format("~p ~n", [Index]) end) || Index <- lists:seq(1, 10)].
1 
2 
3 
4 
5 
7 
6 
8 
9 
10
10> [spawn(fun() -> io:format("~p ~n", [Index]) end) || Index <- lists:seq(1, 10)].
1 
2 
3 
4 
6 
7 
5 
8 
9 
10

  这次结果终于体现出异步了,因为这段代码执行起来几乎不用进行计算,几乎是进程一启动就完成了,所以很大几率会出现按1到10顺序输出的结果,所以要多试几次。老大布置的题目答案其实就是这么一行代码,想当年我是真▪菜鸟啊,哈哈。

  这就是Erlang的多进程并发异步执行,然而,这看上去并没有什么x用,因为上面的例子太简单了,多进程并发的真正威力并没有体现出来,是时候表演真正的技术了。

  接下来,分别用Erlang的单进程和多进程分别计算1到10万的乘积,并比较两者间的运行时间,先上代码:

 1 -module(concurrency_test).
 2 -compile(export_all).
 3 
 4 %% 单线程计算
 5 %% 计算1到Num的乘积
 6 single_process(Num) ->
 7     single_process(1, Num).
 8 
 9 single_process(Result, 0) ->
10     Result;
11 single_process(Result, Index) ->
12     single_process(Result * Index, Index - 1).
13 
14 %% 多线程计算
15 %% 计算1到Num的乘积
16 %% Count为开启进程的个数
17 multiple_processes(Num, Count) ->
18     Pid = self(),        
19     lists:foreach(fun(Index) ->
20         spawn(fun() ->
21             BeginNum = trunc(Num / Count * (Index - 1)) + 1,
22             EndNum = trunc(Num / Count * Index),
23             multiple_processes(Pid, BeginNum, EndNum)
24         end)
25     end, lists:seq(1, Count)),
26     loop(1, Count).
27 
28 loop(Total, Count) ->
29     receive
30         {result, Result} ->
31             case Count - 1 of
32                 0 ->
33                     Total * Result;
34                 _ ->
35                     loop(Total * Result, Count - 1)
36             end;
37         _ ->
38             loop(Total, Count)
39     end.
40 
41 multiple_processes(Pid, BeginNum, EndNum) ->
42     Pid ! {result, calculate(BeginNum, EndNum)}.
43 
44 calculate(BeginNum, EndNum) ->
45     calculate(1, BeginNum, EndNum).
46 
47 calculate(Result, BeginNum, BeginNum) ->
48     Result * BeginNum;
49 calculate(Result, BeginNum, EndNum) ->
50     calculate(Result * BeginNum, BeginNum + 1, EndNum).
51 
52 tc(F) ->
53     {T, _} = timer:tc(F),
54     io:format("消耗时间:~p 秒 ~n", [T / 1000000]).

  这段代码有点小复杂,但应该不影响理解,先来试试运行结果是否正确,试试1到4的计算。

2> c(concurrency_test).
{ok,concurrency_test}
3> concurrency_test:single_process(4).
24
4> concurrency_test:multiple_processes(4, 2).
24

  结果无误,再来试试1到100。

5> concurrency_test:single_process(100).     
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
6> concurrency_test:multiple_processes(100, 2).
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000

  也无问题,看来结果是正确的,现在忽略结果,计算1到100000,看看单进程和2个进程的运行时间对比。

17> concurrency_test:tc(fun() -> concurrency_test:single_process(100000) end).
消耗时间:7.62 秒 
ok
18> concurrency_test:tc(fun() -> concurrency_test:multiple_processes(100000, 2) end).
消耗时间:2.94 秒 
ok

  WOW,2个人干活果然比一个人干活快,如果4个人呢?

19> concurrency_test:tc(fun() -> concurrency_test:multiple_processes(100000, 4) end).
消耗时间:1.614 秒 
ok

  4个人干活更快了,那8个人呢?

20> concurrency_test:tc(fun() -> concurrency_test:multiple_processes(100000, 8) end).
消耗时间:1.375 秒 
ok

  还更快!如果16个人会更快吗?

21> concurrency_test:tc(fun() -> concurrency_test:multiple_processes(100000, 16) end).
消耗时间:1.389 秒 
ok

  咦,好像还慢了。测试的机器CPU是E3-1231 v3,四核心八线程,个人推测满载的时候最多也就调用8个逻辑核心进行计算,这里没有深究。

  下面计算1到100万,看看单进程和8进程时CPU的负载。

  单进程时:

  基本上一直是在占用16%左右的CPU资源。

  8个进程时:

  基本上是把CPU给压榨光了。

  现在终于能够感受到Erlang多进程并发计算时的威力了吧,在计算1到10万的乘积时,因为中间的计算过程可以是乱序的,不影响最终的运算,所以可以用多进程来异步计算,其效率的提升也是非常的明显。

  其实这个示例还是存在缺陷,在计算1到1000000时,第一个开启的进程计算1到125000,第八个开启的进程计算875001到1000000,这两个进程的执行的计算量不在同一个数量级上面,第八个进程就会成为短板,因此这个示例可以优化,使每个进程执行相近的计算量,计算效率会更高。

转载于:https://www.cnblogs.com/a-mnesia/articles/4780953.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值