Programming Erlang 书中的竞争条件示例

Race condition example in Programming Erlang book

在书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).

书上说:

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

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

作者在这里以此为例来描述竞争条件的意思是什么?

不,其中之一可能真的会死。

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 总是 returns ok,所以它不会匹配 not_ok 并在第一行崩溃。) i_die_fast/0 似乎永远递归,但它永远不会得到那么远,并且可能会在达到 on_exit/2 之前死亡。 (但不保证on_exit/2被调用前死掉!欢迎并发。)

关键是你真的不知道。您最了解的是使用 spawn_link 或者,在耦合度较低的情况下 spawn 然后 monitorspawn_monitor.

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


旁注:

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

% 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().