【问题标题】:Race condition example in Programming Erlang bookProgramming Erlang 书中的竞争条件示例
【发布时间】:2015-11-06 14:18:44
【问题描述】:

Programming Erlang: Software for a Concurrent World一书的第 13 章末尾,我们得到了一段代码:

keep_alive(Name, Fun) ->
    register(Name, Pid = spawn(Fun)),
    on_exit(Pid, fun(_Why) -> keep_alive(Name, Fun) end).

书上说:

  • 进程Pid 可能在on_exit 执行之前终止。
  • 如果两个程序尝试同时评估 keep_alive 并使用相同的 Name 值,则代码可能会发生竞争条件。

如果两个程序同时使用相同的Name 调用keep_alive,那么其中一个register 调用将触发badarg 异常,因为名称已被占用,调用进程将终止。

所以,不是进程 Pid 在调用 on_exit 之前死亡,而是 调用 on_exit 的进程死亡

作者在这里以此为例来描述竞态条件的意义何在?

【问题讨论】:

    标签: erlang race-condition


    【解决方案1】:

    不,任何一个都可能真的死了。

    Pid 引用的进程可能在Fun 的第一行中存在错误,它可能会尝试获取一些外部资源作为它的第一个动作并且 失败(锁定的文件、不存在的数据库等),它可能会很快终止非常而不会循环,等等。

    -module(silly).
    -export([do_stuff/0]).
    
    do_stuff() ->
       keep_alive(die_bot, fun i_die_fast/0).
    
    i_die_fast() ->
        not_ok = io:format("Is this ok?~n"),
        receive Anything ->
            ok = io:format("Received: ~tp~n", [Anything]),
            i_die_fast()
        end.
    
    keep_alive(Name, Fun) ->
        register(Name, Pid = spawn(Fun)),
        on_exit(Pid, fun(_Why) -> keep_alive(Name, Fun) end).
    

    上面的代码将如何发挥作用? (io:format/1 总是返回 ok,所以它不会匹配 not_ok 并在第一行崩溃。)i_die_fast/0 似乎永远递归,但它永远不会到达那么远,并且可能会在 on_exit/2 之前死掉到达。 (但保证在调用on_exit/2之前死掉!欢迎并发。)

    关键是你真的不知道。您可以知道的最接近的方法是使用spawn_link,或者在耦合度较低的情况下使用spawn,然后使用monitorspawn_monitor

    register 的竞争条件也为真,并且可能使当前正在执行的进程崩溃——所以这是 两个 竞争条件。


    旁注:

    这就是为什么我几乎总是让派生函数自己注册,这样如果发生冲突,它会在子上下文而不是调用者的上下文中爆炸(在大多数情况下,你可能有各种各样的原因想换一种方式):

    % Note, we don't need the PID of `some_helper` because it is named.
    start() ->
        _ = spawn_link(fun() -> some_helper() end),
        main_loop().
    
    some_helper() ->
        true = register(helper, self()),
        helper_loop().
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-11-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-07
      • 2011-07-17
      • 2013-02-27
      • 2021-04-16
      相关资源
      最近更新 更多