首页 > Erlang并发教程 > 9.2 Erlang并发编程-健壮的服务进程
2013
11-18

9.2 Erlang并发编程-健壮的服务进程

BACK TOP文章索引

  1. 健壮的服务进程
  2. 共2条评论

健壮的服务进程

讲解可靠服务进程设计的最好方法就是借助实例。

第??章(程序??.6)给出了一个资源分配器。对于这个分配器,如果一个资源被分配给了进程,而这个进程在释放资源之前终止(无论是出于意外还是正常终止),那么这个资源就无法被收回。这个问题可以通过以下的方法来解决:

  • 令服务程序捕捉EXIT信号(process_flag(trap_exit, true))。
  • 在分配器和申请资源的进程之间建立连接。
  • 处理由这些进程发出的EXIT信号。

正如图 8.1 所示。

../_images/8.1.png图8.1 健壮的分配器进程和客户进程

分配器的访问函数不变。通过以下方式启动分配器:

start_server(Resources) ->
    process_flag(trap_exit, true),
        server(Resources, []).

为了接收EXIT信号,我们将 “服务器” 循环改为:

server(Free, Allocated) ->
    receive
            {From,alloc} ->
                allocate(Free, Allocated, From);
            {From,{free,R}} ->
                free(Free, Allocated, From, R);
            {'EXIT', From, _ } ->
                check(Free, Allocated, From)
        end.

为了跟申请资源(如果还有资源可用)的进程建立连接,还需要修改allocate/3 。

allocate([R|Free], Allocated, From) ->
        link(From),
        From ! {resource_alloc,{yes,R}},
        server(Free, [{R,From}|Allocated]);
allocate([], Allocated, From) ->
        From ! {resource_alloc,no},
        server([], Allocated).

free/4更复杂些:

free(Free, Allocated, From, R) ->
    case lists:member({R, From}, Allocated) of
            true ->
                From ! {resource_alloc, yes},
                    Allocated1 = lists:delete({R, From}, Allocated),
                    case lists:keysearch(From, 2, Allocated1) of
                        false ->
                            unlink(From);
                        _ ->
                            true
                    end,
                    server([R|Free], Allocated1);
            false ->
                From ! {resource_alloc, error},
                server(Free, Allocated)
        end.

首先我们检查将要被释放的资源,的确是分配给想要释放资源的这个进程的。如果是的话,lists:member({R, From}, Allocated)返回true。我们像之前那样建立一个新的链表来存放被分配出去的资源。我们不能只是简单的unlink From,而必须首先检查Form是否持有其他资源。如果keysearch(From, 2, Allocated1)(见附录??)返回了falseFrom就没有持有其他资源,这样我们就可以unlink From了。

如果一个我们与之建立了link关系的进程终止了,服务程序将会收到一个EXIT信号,然后我们调用Check(Free, Allocated, From)函数。

check(Free, Allocated, From) ->
    case lists:keysearch(From, 2, Allocated) of
        false ->
            server(Free, Allocated);
        {value, {R, From}} ->
            check([R|Free],
            lists:delete({R, From}, Allocated), From)
    end.

如果lists:keysearch(From, 2, Allocated)返回了false,我们就没有给这个进程分配过资源。如果返回了{value, {R, From}},我们就能知道资源R被分配给了这个进程,然后我们必须在继续检查该程序是否还持有其他资源之前,将这个资源添加到未分配资源列表,并且将他从已分配资源列表里删除。注意这种情况下我们不需要手动的与该进程解除连接,因为当它终止的时候,连接就已经解除了。

释放一个没有被分配出去的资源是可能一个严重的错误。我们应当修改程序??.6中的free/1函数,以便杀死试图这样干的程序:[2]。

free(Resource) ->
    resource_alloc ! {self(),{free,Resource}},
        receive
            {resource_alloc, error} ->
                exit(bad_allocation); % exit added here
            {resource_alloc, Reply} ->
                Reply
        end.

用这种方法杀死的程序,如果它还持有其他资源,同时还与服务程序保持着连接,那么服务程序因此将收到一个EXIT信号,如上面所述,处理这个信号的结果会是资源被释放。

以上内容说明了这么几点:

  • 通过设计这样一种服务程序接口,使得客户端通过访问函数(这里是allocate/0free/1)访问服务程序,并且防止了危险的“幕后操作”。客户端和服务程序之间的连接对用户来说是透明的。特别是客户端不需要知道服务程序的进程ID,因此也就不能干涉它的运行。
  • 一个服务程序如果捕获EXIT信号,并且和它的客户端建立连接以便能监视它的话,就可以在客户端进程死亡的时候采取适当的处理行为。

9.2 Erlang并发编程-健壮的服务进程》有 2 条评论

  1. 老实讲,有时候会觉得火影忍者太过啰嗦,但我知道作者为什么要写很多废话了,一,为精彩的意外做铺垫,二,一个意外的战斗情节或战术或者忍术施展的来龙去脉的安排真是太费脑了。三,就是让观众了解火影世界的人物不同的价值观和背景。所以说,尊重每一位创作者。

  2. 老实讲,有时候会觉得火影忍者太过啰嗦,但我知道作者为什么要写很多废话了,一,为精彩的意外做铺垫,二,一个意外的战斗情节或战术或者忍术施展的来龙去脉的安排真是太费脑了。三,就是让观众了解火影世界的人物不同的价值观和背景。所以说,尊重每一位创作者。

留下一个回复

你的email不会被公开。