【问题标题】:Why can you create multiple monitor references to the same process in Erlang?为什么你可以在 Erlang 中为同一个进程创建多个监视器引用?
【发布时间】:2018-08-02 13:14:55
【问题描述】:

这是一个示例跟踪,我可以在同一个 Pid 上调用 erlang:monitor/2

1> Loop = fun F() -> F() end.
#Fun<erl_eval.30.99386804>
2> Pid = spawn(Loop).
<0.71.0>
3> erlang:monitor(process, Pid).
#Ref<0.2485499597.1470627842.126937>
4> erlang:monitor(process, Pid).
#Ref<0.2485499597.1470627842.126942>
5> erlang:monitor(process, Pid).
#Ref<0.2485499597.1470627842.126947>

指令#4 和#5 返回的表达式与#3 不同,这意味着可以在当前进程和Pid 之间创建多个监视器引用。是否存在您需要或使用多个监视器引用到同一进程的实际案例?

我希望这将返回相同的引用(返回一个新的可能意味着旧的失败/崩溃),遵循与 link/1 相同的逻辑。

【问题讨论】:

    标签: erlang erlang-shell


    【解决方案1】:

    假设您使用第三方库来执行此操作(基本上是 OTP *:call/* 函数所做的):

    call(Pid, Request) ->
        call(Pid, Request, ?DEFAULT_TIMEOUT).
    
    call(Pid, Request, Timeout) ->
        MRef = erlang:monitor(process, Pid),
        Pid ! {call, self(), MRef, Request},
        receive
          {answer, MRef, Result} ->
            erlang:demonitor(Mref, [flush]),
            {ok, Result};
          {'DOWN', MRef, _, _, Info} ->
            {error, Info}
        after Timeout ->
            erlang:demonitor(MRef, [flush]),
            {error, timeout}
        end.
    

    然后你在你的代码中使用它来监控相同的进程Pid,然后调用函数call/2,3

    my_fun1(Service) ->
        MRef = erlang:monitor(process, Service),
        ok = check_if_service_runs(MRef),
        my_fun2(Service),
        mind_my_stuf(),
        ok = check_if_service_runs(MRef),
        erlang:demonitor(MRef, [flush]),
        return_some_result().
    
    check_if_service_runs(MRef) ->
        receive
          {'DOWN', MRef, _, _, Info} -> {down, Info}
        after 0 -> ok
        end.
    
    my_fun2(S) -> my_fun3(S).
    
    % and a many layers of other stuff and modules
    my_fun3(S) -> call(S, hello).
    

    如果erlang:monitor/2,3 总是返回相同的引用并且erlang:demonitor/1,2 会删除您以前的监视器,那将是多么令人讨厌的惊喜。这将是丑陋和无法解决的错误的来源。你应该开始认为有库,其他进程,你的代码是一个庞大系统的一部分,而 Erlang 是由经过深思熟虑的有经验的人制作的。可维护性是这里的关键。

    【讨论】:

    • 在开始研究 OTP 后,我不得不承认这并没有在我的脑海中浮现。但是,由于它是异步的并且可能有多个待处理的请求,因此 cast 不是比 call 更合适的示例吗?我认为执行您的call/3 实现的进程一次只能执行一个操作,因此只需要一个监视引用。这是真的?否则,我想我肯定明白它是如何有用的。非常感谢您的回答。
    • @goncalotomas:你还是没明白。问题不在于它是调用还是强制转换,而是在这些之外的代码中。您可以将监视器保存在您的代码或其他第三方库中。在具有不变性的函数式语言中,习惯于制作具有不透明状态、回调等的库,这些状态可以包含MRefs,并在需要时保留它。你必须想得更大。最大的函数式语言商业项目 ADX301 是 Erlang 为其设计和使用的,有 200 万行代码。你必须想得更大。
    • @goncalotomas:我添加了更多代码,希望能帮助您更多地理解这个问题。这是非常人为的,但我希望它有所帮助。
    • 我想我现在看到了——self() 最终执行的代码可能有几个“层”,可能会调用 erlang:monitor/2 以获得相同的 Pid,在这种情况下,你很漂亮非常需要一个独特的参考,否则事情会很快变得混乱。我想这解释了它,谢谢!您最近添加的代码帮助很大。
    猜你喜欢
    • 2013-12-31
    • 2020-09-15
    • 2017-10-30
    • 1970-01-01
    • 2010-12-09
    • 2011-09-16
    • 1970-01-01
    • 1970-01-01
    • 2016-12-21
    相关资源
    最近更新 更多