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

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

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

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

{'EXIT', Exiting_Process_Id, Reason}

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

-module(link_demo).
-export([start/0, demo/0, demonstrate_normal/0, demonstrate_exit/1,
         demonstrate_error/0, demonstrate_message/1]).

start() ->
    register(demo, spawn(link_demo, demo, [])).

demo() ->
    process_flag(trap_exit, true),
    demo1().

demo1() ->
    receive
         {'EXIT', From, normal} ->
             io:format(
                 "Demo process received normal exit from ~w~n",
                 [From]),
             demo1();
         {'EXIT', From, Reason} ->
             io:format(
                 "Demo process received exit signal ~w from ~w~n",
                 [Reason, From]),
             demo1();
         finished_demo ->
             io:format("Demo finished ~n", []);
         Other ->
             io:format("Demo process message ~w~n", [Other]),
             demo1()
    end.

demonstrate_normal() ->
    link(whereis(demo)).

demonstrate_exit(What) ->
    link(whereis(demo)),
    exit(What).

demonstrate_message(What) ->
    demo ! What.

demonstrate_error() ->
    link(whereis(demo)),
    1 = 2.

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

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

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

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


![_images/7.4.png](https://static.bookstack.cn/projects/Concurrent-Programming-in-Erlang/8441be5fbfa26f9ea59daae803ebafb3.png) 图7.4 执行`exit(normal)`

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

link_demo:demonstrate_error(). !!! Error in process <0.17.1> in function !!! link_demo:demonstrate_error() !!! reason badmatch ** exited: badmatch ** 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](https://static.bookstack.cn/projects/Concurrent-Programming-in-Erlang/ece3a9cbd0335635eef33d8afd1aa6ca.png) 图7.5 匹配错误导致的进程失败

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

link_demo:demonstrate_message(hello). Demo process message hello hello


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

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

link_demo:demonstrate_message(finished_demo). Demo finished finished_demo ```