《Erlang 程序设计》练习答案 -- 第十二章 并发编程

% (1).编写一个start(AnAtom, Fun)函数来把spawn(Fun)注册为AnAtom。
% 确保当两个并行的进程同时执行start/2时你的程序也能正确工作。
% 在这种情况下,必须保证其中一个进程会成功执行而另一个会失败。


-module(execstart).
-export([create/2]).

start(AnAtom, Fun) ->
    case whereis(AnAtom) of
        Pid -> io:format("this AnAtom have registered~n");
        undefined -> register(AnAtom, spawn(Fun)),
                     io:format("this is process name:~p~n", [AnAtom])
    end.

create(AnAtom, Fun) ->
    start(AnAtom, Fun),
    start(AnAtom, Fun).

这段程序结果很奇怪,结果二者都不能创建。

case的两个子句交换下位置就好。

-module(execstart).
-export([create/2]).

start(AnAtom, Fun) ->
    case whereis(AnAtom) of
        undefined -> register(AnAtom, spawn(Fun)),
                 io:format("this is process name:~p~n", [AnAtom]);
        Pid -> io:format("this AnAtom have registered~n")
    end.

create(AnAtom, Fun) ->
    start(AnAtom, Fun),
    start(AnAtom, Fun).

因为Pid匹配的范围大,它可以匹配undefined,所以第一段代码中undefined永远不会被匹配。我们在写程序的时候需要注意,范围小的放在前面,范围大的放在后面,避免大范围包含小范围这种情况。

第二段结果


% (2).用12.3节里的程序在你的机器上测量一下进程分裂所需要的时间。
% 在一张进程数量对进程创建时间的图上进行标绘。
% 你能从中得到什么结论。

-module(processes).
-export([max/1]).

max(N) ->
    Max = erlang:system_info(process_limit),
    io:format("Maximum allowed processes:~p~n", [Max]),
    % statistics 返回跟指定类型 Type 相关的系统信息
    % runtime 是cpu耗时,wall_clock 是代码运行时间
    statistics(runtime),
    statistics(wall_clock),
    L = for(1, N, fun() -> spawn(fun() -> wait() end) end),
    {_, Time1} = statistics(runtime),
    {_, Time2} = statistics(wall_clock),
    lists:foreach(fun(Pid) -> Pid ! die end, L),
    U1 = Time1 * 1000 / N,
    U2 = Time2 * 1000 / N,
    io:format("cpu:~p, time:~p~n", [U1, U2]).

wait() ->
    receive 
        die -> void
    end.


for(N, N, F) -> [F()];
for(I, N, F) -> [F() | for(I+1, N, F)].

这里就不做表格了…

从上图能看出,无论是创建100还是100000个进程,cpu和程序耗时都是非常短的,而且进程数量和实现不是线性关系。


% (3).编写一个环形计时测试。创建一个由N个进程组成的环。
% 把一个消息沿着环发送M次,这样总共发送的消息是M*N。
% 记录不同的N和M值所花费的时间
% 用你熟悉的其他编程语言编写一个类似的程序,然后比较一下结果。
% 写一篇博客,把结果在网上发表出来。

-module(sendMessage).
-export([start/2]).


% N 个进程, M 次
start(N, M) ->
    sendstart(M, createProcess(N)).

% 创建 N 个进程
createProcess(N) ->
    L = for(1, N, fun() -> spawn(fun() -> loop() end) end),
    L.

% 起始
sendstart(M, L) ->
    % 给第一个进程发送请求,开始绕环发送消息
    Pid = getPid(1, L, M),
    Pid ! {1, L, M, "hello world"}.

% 进程执行的函数
loop() ->
    receive 
        % {1, [1,2,3,4,5], "hello world"}
        {I, L, M, Message} -> 
            io:format("Pid:~p Recv Message:~p~n", [(I rem lists_size(L))+1,Message]),
            % 发送给下一个进程
            case getPid(I+1, L, M) of
                none -> io:format("have send...~n");
                Pid  -> Pid ! {I+1, L, M, Message},
                        loop()   
            end
    end.

% 获得进程的 Pid。
getPid(I, L, M) ->
    io:format("...........I:~p~n", [I]),
    case lists_size(L) of
        Size when I =< Size ->
            lists:nth(I, L);
        % 注意余数为 0
        Size when I > Size, I rem Size =:= 0 ->
            lists:nth(Size, L);
        Size when I > Size, I =< M ->
            lists:nth((I rem Size), L);
        Size when I > M ->
            none
    end.

for(Max, Max, F) -> [F()];
for(I, Max, F)   -> [F() | for(I+1, Max, F)].


% 计算lists大小函数
lists_size(L) ->
   lists_size(L, 0).
lists_size([_|T], Size) ->
    lists_size(T, Size+1);
lists_size([], Size) ->
    Size.

至于题目要求性能对比,其实结果很明显,erlang的进程不是操作系统范畴的,不需要切换,不需要保存上下文,而c/c++包括Java,它们的进程都非常重,上下文切换非常耗时,Linux下可以看看task_struct这个结构体,保存进程的信息,而erlang进程属于虚拟机,非常轻,不共享内存,所以速度肯定非常快。
等闲了再写篇测试博客^_^

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夏天的技术博客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值