• 自定义默认的信号接收动作

    自定义默认的信号接收动作

    BIF process_flag/2可用于自定义进程接收到EXIT信号时所采取的默认行为。如下所述,执行process_flag(trap_exit,true)将改变默认行为,而process_flag(trap_exit,false)重新恢复默认行为。

    如前所述,EXIT信号的格式如下:

    1. {'EXIT', Exiting_Process_Id, Reason}

    调用了process_flag(trap_exit,true)的进程接收到其他进程发送的EXIT信号后不再自动终止。所有EXIT信号,包括Reason为原子式normal的信号,都将被转换为消息,进程可以以接收其他消息同样的方式来接收这些消息。程序7.3说明了进程如何互相链接以及执行了process_flag(trap_exit,true)的进程如何接收EXIT信号。

    1. -module(link_demo).
    2. -export([start/0, demo/0, demonstrate_normal/0, demonstrate_exit/1,
    3. demonstrate_error/0, demonstrate_message/1]).
    4.  
    5. start() ->
    6. register(demo, spawn(link_demo, demo, [])).
    7.  
    8. demo() ->
    9. process_flag(trap_exit, true),
    10. demo1().
    11.  
    12. demo1() ->
    13. receive
    14. {'EXIT', From, normal} ->
    15. io:format(
    16. "Demo process received normal exit from ~w~n",
    17. [From]),
    18. demo1();
    19. {'EXIT', From, Reason} ->
    20. io:format(
    21. "Demo process received exit signal ~w from ~w~n",
    22. [Reason, From]),
    23. demo1();
    24. finished_demo ->
    25. io:format("Demo finished ~n", []);
    26. Other ->
    27. io:format("Demo process message ~w~n", [Other]),
    28. demo1()
    29. end.
    30.  
    31. demonstrate_normal() ->
    32. link(whereis(demo)).
    33.  
    34. demonstrate_exit(What) ->
    35. link(whereis(demo)),
    36. exit(What).
    37.  
    38. demonstrate_message(What) ->
    39. demo ! What.
    40.  
    41. demonstrate_error() ->
    42. link(whereis(demo)),
    43. 1 = 2.

    示例代码的启动方式如下:

    1. > link_demo:start().
    2. true
    link_demo:start()以函数demo/0启动一个进程并用名字demo进行注册。demo/0关闭EXIT信号的默认处理机制并调用demo1/0等待新消息的到来。我们来考察一次正常退出过程:
    1. > link_demo:demonstrate_normal().trueDemo process received normal exit from <0.13.1>
    执行demonstrate_normal/0的进程(在这个例子中该进程由Erlang shell创建)寻找注册进程demo的进程标识并与之建立链接。函数demostrate_normal/0没有别的子句,它的执行进程无事可做因而正常终止,从而引发信号:
    1. {'EXIT', Process_Id, normal}
    该信号被发送到注册进程demo。注册进程demo正在等待EXIT信号,因此它将之转换为一条消息,该消息在函数demo1/0内被接收,并输出文本(参见图7.2):
    1. Demo process received normal exit from <0.13.1>
    接着demo1/0继续递归调用自身。_images/7.2.png图7.2 正常退出信号下面再来考察一次异常退出过程:
    1. > link_demo:demonstrate_exit(hello).Demo process received exit signal hello from <0.14.1> exited: hello
    demonstrate_normal/0相同,demonstrate_exit/1创建一个到注册进程demo的链接。该例中,demonstrate_exit/1通过exit(hello)调用BIF exit/1。这导致demostrate_exit/1的执行进程异常终止,并将信号:
    1. {'EXIT', Process_Id, hello}
    发送给注册进程demo(参见图7.3)。注册进程demo将该信号转换为消息,并在函数demo1/0内被接收,从而输出文本:
    1. Demo process received exit signal hello from <0.14.1>
    接着demo1/0继续递归调用自身。_images/7.3.png图7.3 执行exit(hello)

    下一个案例中(如图7.4)我们将看到link_demo:demonstrate_normal()link_demo:demonstrate_exit(normal)是等同的:

    1. > link_demo:demonstrate_exit(normal).
    2. Demo process received normal exit from <0.13.1>
    3. ** exited: normal **

    _images/7.4.png图7.4 执行exit(normal)

    下一个案例将展示出现运行时错误时,会发生什么事:

    1. > link_demo:demonstrate_error().
    2. !!! Error in process <0.17.1> in function
    3. !!! link_demo:demonstrate_error()
    4. !!! reason badmatch
    5. ** exited: badmatch **
    6. Demo process received exit signal badmatch from <0.17.1>

    向前面一样,link_demo:demonstrate_error/0创建一个到注册进程demo的链接。link_demo:demonstrate_error/0错误地试图匹配1=2。 该错误导致link_demo:demonstrate_error/0的执行进程异常终止,并发送信号{'EXIT',Process_Id,badmatch}至注册进程demo(参见图7.5)。

    _images/7.5.png图7.5 匹配错误导致的进程失败

    下一个案例中我们简单地向正在等待消息的注册进程demo发送消息hello

    1. > link_demo:demonstrate_message(hello).
    2. Demo process message hello
    3. hello

    没有链接被创建,也就没有EXIT信号被发送或被接收。

    通过以下调用来结束这个示例:

    1. > link_demo:demonstrate_message(finished_demo).
    2. Demo finished
    3. finished_demo