1. 程式人生 > >《Erlang 程式設計》練習答案 -- 第十三章 併發程式中的錯誤

《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.