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

原创 2015年11月18日 00:59:42
% (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.
版权声明:free, open, share

《Erlang 程序设计》练习答案 -- 第六章 顺序程序的错误处理

% (1).file:read_file(File) 会返回 {ok, Bin} 或者 {error, Why}, % 其中 File 是文件名, Bin 则包含了文件的内容。请编写一个 % myfi...

Erlang-- 学习笔记5,平行式、面向并发程序设计

备注:Erlang-- 学习笔计,知识要点来自于维基百科,一些不容易懂的地方,我给出重新解释,方便自己理解和记忆,如果有错误,请大家不吝指教。 平行式程序设计       Erlang最主...

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

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

《Java高并发程序设计》学习 --2.8 隐蔽的错误

1)无提示的错误案例 int v1 = 1073741827; int v2 = 1431655768; int ave = (v1+v2)/2; System.out.println(a...

《Erlang 程序设计》练习答案 -- 第九章 类型

% (1).编写一些导出单个函数的小模块,以及被导出函数的类型规范。 % 在函数里制造一些类型错误,然后对这些程序运行 dialyzer 并试着理解错误消息。 % 有时候你制造的错误无法被 dialy...

《Erlang 程序设计》练习答案 -- 第八章 顺序编程补遗

% (1).复习这一张关于Mod:module_info()的部分。输入命令dict:module_info()。这个模块返回了多少函数?% (2).code:all_loaded() 命令会返回一个...

《Erlang 程序设计》练习答案 -- 第七章 二进制型与位语法

% 编写一个函数来反转某个二进制型里的字节顺序-module(reverse_byte). -export([reverse_byte/1]).% 注释为将对象先转化为二进制,然后进行反转。 %rev...

《Erlang 程序设计》练习答案 -- 第三章 基本概念

% (1).快速浏览 3.13 节,然后测试并记忆这些行编辑命令在 Erlang shell 中输入 Ctrl + A 行首 Ctrl + D 删除当前字符 Ctrl + E 行尾 Ctrl +...

关于线程和多线程,JAVA高并发程序设计

关于线程和多线程,面试里你所要知道的一切(一)主要整理一些关于线程的知识,尽量做到言简意赅,面试的时候用。线程前段时间找工作,很多次问到关于线程的问题,回答的时候就开启背书模式:线程是轻量级的进程,是...

实战Java高并发程序设计之ReentrantLock(二)

前面讲到了ReentranLock的基本使用,现在来看看它的底层实现. 先看ReentrantLok的UML类图(图片来自:http://www.cnblogs.com/skywang12345/p/...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:《Erlang 程序设计》练习答案 -- 第十三章 并发程序中的错误
举报原因:
原因补充:

(最多只允许输入30个字)