《Erlang 程式設計》練習答案 -- 第十三章 併發程式中的錯誤
阿新 • • 發佈:2019-01-30
% (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.