1

我使用标准 IO 函数修改了Learn you some Erlang中的厨房模块,以查看按执行顺序打印出来的内容,我发现了一些非常奇怪的东西。基本上我在shell中运行了以下

3> Pid = spawn(kitchen, fridge2, [[baking_soda]]).
<0.41.0>
4> kitchen:store(Pid, water).
0
2
1
ok
ok

在我看来,函数 store 在打印出 0 之后的 store 函数中的 receive 子句之前调用了函数 freezer2 ,然后在打印了 2 之后,执行了接收子句并最终打印了 1 。修改后的代码如下。如何从 store 函数中调用冰箱函数?这是因为以某种方式并行执行吗?这条线{Pid, Msg} ->在 store 功能中有什么作用?是函数调用吗?为什么可以打印?

-module(kitchen).
-compile(export_all).

start(FoodList) ->
    spawn(?MODULE, fridge2, [FoodList]).

store(Pid, Food) ->
    Pid ! {self(), {store, Food}},
    io:format("~p~n", [0]),
    receive                 
        {Pid, Msg} -> 
            io:format("~p~n", [1]),
            io:format("~p~n", [Msg]),
            Msg
    end.

take(Pid, Food) ->
    Pid ! {self(), {take, Food}},
    receive
        {Pid, Msg} -> Msg
    end.

store2(Pid, Food) ->
    Pid ! {self(), {store, Food}},
    receive
        {Pid, Msg} -> Msg
    after 3000 ->
        timeout
    end.

take2(Pid, Food) ->
    Pid ! {self(), {take, Food}},
    receive
        {Pid, Msg} -> Msg
    after 3000 ->
        timeout
    end.

fridge1() ->
    receive
        {From, {store, _Food}} ->
            From ! {self(), ok},
            fridge1();
        {From, {take, _Food}} ->
            %% uh....
            From ! {self(), not_found},
            fridge1();
        terminate ->
            ok
    end.

fridge2(FoodList) ->
    receive
        {From, {store, Food}} ->
            From ! {self(), ok},
            io:format("~p~n", [2]),
            fridge2([Food|FoodList]);
        {From, {take, Food}} ->
            case lists:member(Food, FoodList) of
                true ->
                    io:format("~p~n", [3]),
                    From ! {self(), {ok, Food}},
                    fridge2(lists:delete(Food, FoodList));
                false ->
                    io:format("~p~n", [4]),
                    From ! {self(), not_found},
                    fridge2(FoodList)
            end;
        terminate ->
            ok
    end.
4

1 回答 1

1

与 case 语句类似,receive 使用模式匹配来确定要执行的子句。 {Pid, Msg}是一个匹配任何 2 元组的子句。

让我们来看看你的代码的执行——

Pid = spawn(kitchen, fridge2, [[baking_soda]]).

这会产生一个执行该kitchen:fridge2/1函数的新进程。该函数会阻塞,直到它接收到一个 2 元组形式{From, {[store|take], Food}}或原子“终止”的消息。

kitchen:store(Pid, water).

同时,您从 shell 调用上述函数。它将消息发送{self(), {store, Food}}到该新进程,打印“0”,然后等待接收 2 元组的消息。

另一个进程现在已经收到一条满足其接收的消息。它将消息发送{self(), ok}回发送消息的进程,打印“2”,递归调用自身并再次等待接收消息。

shell 进程现在已经收到一条消息并继续执行。它打印“1”,然后打印它收到的元组的第二个元素(“ok”)。最后它返回'ok'到shell。

shell 打印结果(“ok”)并显示提示。

第二个进程仍在等待接收消息。

于 2013-02-16T22:33:19.120 回答