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).
书上说:
- 进程
Pid
可能会在 on_exit
执行之前结束。
- 如果两个程序试图同时计算
keep_alive
并且 Name
的值相同,则代码可能会出现竞争条件 .
如果两个程序同时使用相同的 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
然后 monitor
或 spawn_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().
在书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
的进程死掉了
作者在这里以此为例来描述竞争条件的意思是什么?
不,其中之一可能真的会死。
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
然后 monitor
或 spawn_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().