zoukankan      html  css  js  c++  java
  • gen_server的enter_loop分析

    http://my.oschina.net/astute/blog/119250?p=1

    在看ranch user guide的过程中,发现实现protocol handler需要使用特殊的gen_server形式,也就是enter_loop函数调用,事例代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    -module(echo_protocol).
    -behaviour(ranch_protocol).
      
    -export([start_link/4]).
    -export([init/4]).
      
    start_link(ListenerPid, Socket, Transport, Opts) ->
        Pid = spawn_link(?MODULE, init, [ListenerPid, Socket, Transport, Opts]),
        {ok, Pid}.
      
    init(ListenerPid, Socket, Transport, _Opts = []) ->
        ok = ranch:accept_ack(ListenerPid),
        loop(Socket, Transport).
      
    loop(Socket, Transport) ->
        case Transport:recv(Socket, 0, 5000) of
            {ok, Data} ->
                Transport:send(Socket, Data),
                loop(Socket, Transport);
            _ ->
                ok = Transport:close(Socket)
        end.

    实现ranch的protocol handler只需要实现start_link函数即可,函数中需要启动一个新的进程,新的进程需要调用accept_ack函数来绑定socket的owner进程。

    如果回调要实现gen_server行为模式的话,Listener进程调用模块的start_link方法,内部同步的启动gen_server进程,并且等待gen_server进程调用init函数返回,如果这个时候在init的方法中调用accept_ack方法,就会造成循环调用,造成死锁。

    ranch提供的方法如下,使用gen_server的enter_loop方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    -module(my_protocol).
    -behaviour(gen_server).
    -behaviour(ranch_protocol).
      
    -export([start_link/4]).
    -export([init/1]).
    %% Exports of other gen_server callbacks here.
      
    start_link(ListenerPid, Socket, Transport, Opts) ->
        proc_lib:start_link(?MODULE, [[ListenerPid, Socket, Transport, Opts]]).
     
    init(ListenerPid, Socket, Transport, _Opts = []) ->
        ok = proc_lib:init_ack({ok, self()}),
        %% Perform any required state initialization here.
        ok = ranch:accept_ack(ListenerPid),
        ok = Transport:setopts(Socket, [{active, once}]),
        gen_server:enter_loop(?MODULE, [], {state, Socket, Transport}).
     
    %% Other gen_server callbacks here.

    通常情况下启动gen_server,需要调用gen_server:start_link(),参数中设置回调模块,这样的话像上面的分析,是会有死锁的问题的。

    规避这个问题就是使用enter_loop方法。这个方法可以让已经存在的独立进程成为gen_server进程,在进入普通的gen_server循环之前执行设定的逻辑。

    Listener进程调用proc_lib:start_link方法,创建新进程A,A执行init方法,调用proc_lib:init_ack()方法,告诉listener进程A进程已经启动,此时Listener进程就可以返回了。然后A进程再执行accept_ack()方法,最后调用enter_loop方法,让自己进入gen_server的执行循环。

  • 相关阅读:
    分享Silverlight/WPF/Windows Phone一周学习导读(12月13日12月19日)
    分享Silverlight/WPF/Windows Phone一周学习导读(12月20日12月26日)
    关注Silverlight的未来 推荐注册微软Silverlight FireStarter大会
    Silverlight for Windows Phone Toolkit升级 新增四个控件
    分享Silverlight新鲜事(11月15日21日) PDC 10 Downloader
    分享Silverlight/WPF/Windows Phone一周学习导读(11月22日28日)
    微软副总裁Bob Muglia对Silverlight的公开道歉信
    分享Silverlight/WPF/Windows Phone一周学习导读(11月29日12月5日)
    [转]diff和patch
    Linux查看进程的所有子进程和线程
  • 原文地址:https://www.cnblogs.com/fvsfvs123/p/4243792.html
Copyright © 2011-2022 走看看