《Erlang 程序设计》练习答案 -- 第十三章 并发程序中的错误

% (1).编写一个 my_spawn(Mod, Func, Args) 函数。它的行为类似 spawn(Mod, Func, Args),
% 但有一点区别。如果分裂出的进程挂了,就应打印出一个消息,说明进程挂掉的原因以及
% 在此之前存货了多长时间。

-module(myspawn).
-export([my_spawn/3]).

-spec my_spawn(Mod, Func, Args) -> pid() when
    Mod  :: atom(),
    Func :: atom(),
    Args :: [T],
    T    :: term().


my_spawn(Mod, Func, Args) ->
    {Hour1, Minute1, Second1} = time(),
    Pid = spawn(Mod, Func, Args),
    spawn(fun() ->
            Ref = monitor(process, Pid),
            receive 
                {'DOWN', Ref, process, Pid, Why} ->
                    {Hour2, Minute2, Second2} = time(),
                    io:format("error:~p, time:~pS~n", [Why, (Hour2-Hour1)*60*60+(Minute2-Minute1)*60+(Second2-Second1)])
            end
    end),
    Pid.

% (2).用本章前面展示的 on_exit 函数来完成上一个练习。

-module(myspawn).
-export([my_spawn/3]).

my_spawn(Mod, Func, Args) ->
    {Hour1, Minute1, Second1} = time(),
    on_exit(spawn(Mod, Func, Args), fun() -> io:format("error......~n") end),
    {Hour2, Minute2, Second2} = time(),
    io:format("time:~p~n", [(Hour2-Hour1)*60*60 + (Minute2-Minute1)*60 + (Second2-Second1)]).


on_exit(Pid, Fun) ->
    spawn(fun() -> 
          Ref = monitor(process, Pid),
          receive
              {'DOWN', Ref, process, Pid, Why} ->
                  Fun(Why)
          end  
    end).

% (3).编写一个 my_spawn(Mod, Func, Args, Time) 函数。它的行为类似spawn(Mod, Func, Args),
% 但有一点区别。如果分裂出的进程存活超过了Time秒,就应当被摧毁。

-module(myspawn).
-export([my_spawn/4, start/0, func/1]).

% 类型声明
-spec my_spawn(Mod, Func, Args, Time) -> pid() when
      Mod   :: atom(),
      Func  :: atom(),
      Args  :: [T],
      T     :: term(),
      Time  :: term().

% 启动函数
start() ->
    my_spawn(myspawn, func, [], 5000).

% 创建一个进程和一个监视进程
my_spawn(Mod, Fun, Args, Time) ->
    Pid = spawn(Mod, Fun, [Time]),
    io:format("Pid:~p~n", [Pid]),
    spawn(fun() -> 
            Ref = monitor(process, Pid),
            receive 
                {'DOWN', Ref, process, Pid, Why} ->
                    io:format("Pid:~p process quit~n", Pid),
                    io:format("Why Quit:~p~n", [Why])
            end  
        end),
    Pid.

% 创建的进程执行函数,当超时Time后,退出并发送一个退出信号给监视进程,监视进程接受后打印该进程的 Pid 和退出的原因 Why。
func(Time) ->
    io:format("Time:~p~n", [Time]),
    receive 
        {ok, Message} ->
            io:format("Message:~p~n", Message)
    after Time ->
            exit("timeout")
    end.

5秒后打印 =ERROR REPORT===


% (4).编写一个函数,让它创建一个每隔5秒就打印一次“我还在运行”的注册进程。
% 编写一个函数来监视这个进程,如果进程挂了就重启它。启动公共进程和监视进程。
% 然后摧毁公共进程,检查它是否会被监视进程重启。

-module(myspawn).
-export([start/0, my_spawn/3, func/0]).


start() ->
    my_spawn(myspawn, func, []).

my_spawn(Mod, Func, Args) ->
    Pid = spawn(Mod, Func, Args),
    spawn(fun() -> 
            Ref = monitor(process, Pid),
            receive 
                {'DOWN', Ref, process, Pid, Why} ->
                    io:format("接受到消息:~p,正在重启进程~n", [Why]),
                    spawn(Mod, Func, Args),
                    io:format("重启完毕...~n"),
                    monitor(process, Pid)
            end
          end),
    Pid.

func() ->
    receive
        {ok, Message} ->
              exit(Message)
    after 5000 ->
              io:format("我还在运行~n"),
              func()
    end.

注意,即使函数不在模块外面使用,也必须export导出,否则运行失败。


我这里有两个版本,一个正确,一个错误
版本1 正确运行

% (5).编写一个函数来启动和监视多个进程。
% 如果任何一个工作进程非正常中止,就重启它

-module(myspawn).
-export([my_spawn/4, start/1, for/3, spawn_func/0, list_func/3]).


start(N) ->
    my_spawn(myspawn, spawn_func, [], N).

my_spawn(Mod, Func, Args, N) ->
    % 创建 N 个进程。 
    % 创建监视进程
    Monitor = spawn(fun() ->
                        Fs = list_func(N, [], spawn_func),
                        % spawn_link 注意如果一参版本,只能用`fun()-> ... end`匿名函数作为参数,如果是三参版本,Func参数可以是普通函数
                        Id = [spawn_link(myspawn, F, []) || F <- Fs],
                        receive
                            {'DOWN', _, process, Pid, Why} ->
                                io:format("Pid:~p quit, Reason:~p~n", [Pid, Why]),
                                io:format("正在重启进程~n"),
                                spawn_link(Func),
                                io:format("重启完毕~n")
                        end
                    end),
    io:format("Monitor:~p~n", [Monitor]).

spawn_func() ->
    io:format("hello world:~p~n", [self()]),
    receive 
        {ok, Message} -> 
            exit(Message);
        other ->
            spawn_func()
    end.

list_func(N, H, F) when N =/= 1 ->
    list_func(N-1, [F|H], F);
list_func(N, H, F) when N =:= 1 ->
    H.

版本2 参数错误

% (5).编写一个函数来启动和监视多个进程。
% 如果任何一个工作进程非正常中止,就重启它

-module(myspawn).
-export([my_spawn/4, start/1, for/3, spawn_func/0, list_func/3]).


start(N) ->
    my_spawn(myspawn, spawn_func, [], N).

my_spawn(Mod, Func, Args, N) ->
    % 创建 N 个进程。 
    % 创建监视进程
    Monitor = spawn(fun() ->
                        % 为何不能 for 循环创建spawn_link?我猜想可能是在Monitor进程外部调用函数,获得不到Monitor信息,所以不能和Monitor连接起来?问题先放这里,感兴趣小伙伴可以留言一起讨论
                        for(1, N, spawn_link),
                        receive
                            {'DOWN', _, process, Pid, Why} ->
                                io:format("Pid:~p quit, Reason:~p~n", [Pid, Why]),
                                io:format("正在重启进程~n"),
                                spawn_link(Func),
                                io:format("重启完毕~n")
                        end
                    end),
    io:format("Monitor:~p~n", [Monitor]).

spawn_func() ->
    io:format("hello world:~p~n", [self()]),
    receive 
        {ok, Message} -> 
            exit(Message);
        other ->
            spawn_func()
    end.

for(Max, Max, Func) -> [Func(myspawn, spawn_func, [])];
for(I, Max, Func)   -> [Func(myspawn, spawn_func, []) | for(I+1, Max, Func)].

% (6).编写一个函数来启动和监视多个工作进程,如果任何一个进程非正常终止,
% 就摧毁所有进程,然后重启它们。

-module(myspawn).
-export([my_spawn/4, start/1, spawn_func/0, list_func/3]).


start(N) ->
    my_spawn(myspawn, spawn_func, [], N).

my_spawn(_, _, _, N) ->
    Monitor = spawn(fun() ->
                        % 创建 N 个进程
                        Fs = list_func(N, [], spawn_func),
                        Id = [spawn_link(myspawn, F, []) || F <- Fs],
                        io:format("Id:~p~n", [Id]),
                        receive
                            {'DOWN', _, process, Pid, normal} ->
                                Pid;
                            {'DOWN', _, process, Pid, Why} ->
                                io:format("Pid:~p quit, Reason:~p~n", [Pid, Why]),
                                % 杀死所有工作进程
                                [exit(Cpid, normal) || Cpid <- Id],
                                io:format("正在重启所有进程~n"),
                                % 新建所有工作进程
                                [spawn_link(myspawn, F, []) || F <- Fs],
                                io:format("重启完毕~n")
                        end
                    end),
    io:format("Monitor:~p~n", [Monitor]).

spawn_func() ->
    io:format("hello world:~p~n", [self()]),
    receive 
        {ok, Message} -> 
            exit(Message);
        other ->
            spawn_func()
    end.

list_func(N, H, F) when N =/= 1 ->
    list_func(N-1, [F|H], F);
list_func(N, H, _) when N =:= 1 ->
    H.
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

夏天的技术博客

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

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

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

打赏作者

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

抵扣说明:

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

余额充值