Erlang并发理解
Erlang concurrency understanding
最近我一直在尝试了解 Erlang 中的并发服务器。考虑以下向服务器发出请求的代码。取决于
特定的执行顺序,3 个进程可能会打印不同的值。订单是多少,每个过程的最高值和最低值是多少?
test() ->
Server = start(),
spawn(fun() ->
incr(Server),
io:format("Child 1 read ~p~n", [read(Server)]) end),
incr(Server),
spawn(fun() ->
incr(Server),
io:format("Child 2 read ~p~n", [read(Server)]) end),
io:format("Parent read ~p~n", [read(Server)]).
代码针对以下服务器运行:
-module(p4).
-export([start/0, init/0, read/1, incr/1, reset/1]).
start() ->
spawn(fun() -> init() end).
init() -> loop(0).
loop(N) ->
receive
{read, Pid} ->
Pid ! {value, self(), N},
loop(N);
{incr, Pid} ->
Pid ! {incr_reply, self()},
loop(N+1);
{reset, Pid} ->
Pid ! {reset_reply, self()},
loop(0)
end.
read(Serv) ->
Serv ! {read, self()},
receive {value, Serv, N} -> N end.
incr(Serv) ->
Serv ! {incr, self()},
receive {incr_reply, Serv} -> ok end.
reset(Serv) ->
Serv ! {reset, self()},
receive {reset_reply, Serv} -> ok end.
- Parent:最低 = 1 最高 = 3
- 孩子 1:最低 = 1 最高 = 3
- 孩子 2:最低 = 1 最高 = 2
我不太确定订单,但我想可能是:
- 孩子 1 可以阅读 1、2 和 3
- Parent 可以读取 1、2 和 3
- 孩子 2 可以阅读 1 和 2
这对于最低值、最高值和订单都正确吗?
循环中的初始值为0。服务器的增量操作在执行增量之前回复调用者,但这无关紧要,因为在发送回复和实际增量之间没有处理任何消息。每条读取的消息都会产生一个回复,其中包含在它之前到达的所有增量消息的效果。由于保证从一个进程到另一个进程的消息顺序,任何递增然后读取的进程都保证至少读取它自己的增量。服务器的读操作简单地用当前循环值回复。重置操作未使用。
Child1 递增,然后阅读。它最初与 Parent 并发运行,然后与 Child2 并发运行,两者都递增。因此它可以只从它自己的增量中读取 1,从它自己的增量和它的父增量中读取 2,或者如果它的读取也从 Child2 中获取增量,则为 3。
Child2 也递增,然后读取,但直到 Parent 已经递增后才开始。因此,它可以读取的最小值是 2,并且由于它与 Child1 同时运行,因此它也可以读取 3。
父级递增,然后读取,因此它可以读取的最小值是 1。它的读取与 Child1 和 Child2 同时运行,因此如果它的读取发生在它们的任何一个递增之前,它会看到一个 1。它也可以读取一个如果它的读取拾取了其中一个子增量,则为 2;如果它的读取拾取了两个子增量,则为 3。
最近我一直在尝试了解 Erlang 中的并发服务器。考虑以下向服务器发出请求的代码。取决于 特定的执行顺序,3 个进程可能会打印不同的值。订单是多少,每个过程的最高值和最低值是多少?
test() ->
Server = start(),
spawn(fun() ->
incr(Server),
io:format("Child 1 read ~p~n", [read(Server)]) end),
incr(Server),
spawn(fun() ->
incr(Server),
io:format("Child 2 read ~p~n", [read(Server)]) end),
io:format("Parent read ~p~n", [read(Server)]).
代码针对以下服务器运行:
-module(p4).
-export([start/0, init/0, read/1, incr/1, reset/1]).
start() ->
spawn(fun() -> init() end).
init() -> loop(0).
loop(N) ->
receive
{read, Pid} ->
Pid ! {value, self(), N},
loop(N);
{incr, Pid} ->
Pid ! {incr_reply, self()},
loop(N+1);
{reset, Pid} ->
Pid ! {reset_reply, self()},
loop(0)
end.
read(Serv) ->
Serv ! {read, self()},
receive {value, Serv, N} -> N end.
incr(Serv) ->
Serv ! {incr, self()},
receive {incr_reply, Serv} -> ok end.
reset(Serv) ->
Serv ! {reset, self()},
receive {reset_reply, Serv} -> ok end.
- Parent:最低 = 1 最高 = 3
- 孩子 1:最低 = 1 最高 = 3
- 孩子 2:最低 = 1 最高 = 2
我不太确定订单,但我想可能是:
- 孩子 1 可以阅读 1、2 和 3
- Parent 可以读取 1、2 和 3
- 孩子 2 可以阅读 1 和 2
这对于最低值、最高值和订单都正确吗?
循环中的初始值为0。服务器的增量操作在执行增量之前回复调用者,但这无关紧要,因为在发送回复和实际增量之间没有处理任何消息。每条读取的消息都会产生一个回复,其中包含在它之前到达的所有增量消息的效果。由于保证从一个进程到另一个进程的消息顺序,任何递增然后读取的进程都保证至少读取它自己的增量。服务器的读操作简单地用当前循环值回复。重置操作未使用。
Child1 递增,然后阅读。它最初与 Parent 并发运行,然后与 Child2 并发运行,两者都递增。因此它可以只从它自己的增量中读取 1,从它自己的增量和它的父增量中读取 2,或者如果它的读取也从 Child2 中获取增量,则为 3。
Child2 也递增,然后读取,但直到 Parent 已经递增后才开始。因此,它可以读取的最小值是 2,并且由于它与 Child1 同时运行,因此它也可以读取 3。
父级递增,然后读取,因此它可以读取的最小值是 1。它的读取与 Child1 和 Child2 同时运行,因此如果它的读取发生在它们的任何一个递增之前,它会看到一个 1。它也可以读取一个如果它的读取拾取了其中一个子增量,则为 2;如果它的读取拾取了两个子增量,则为 3。