为什么 werl 在一个 shell window 中执行相同的代码并在另一个中冻结?

Why werl executes the same code in one shell window and freezes in another?

我正在尝试解决 O'Reilly Erlang Programming 一书中的练习 5-2。 我的设置是 Win10werl Eshell V7.3。 以下是重现我的问题的步骤:

  1. c(frequency).

  2. frequency:start().

  3. CTRL-G -> S -> C -- 切换到新的 Erlang shell

  4. frequency:allocate().

  5. frequency:deallocate(10).

如果我省略第 3 点,那么一切正常,但是当我按照上述步骤进行所有操作时,shell 会卡在 3043 行。

谁能解释一下我做错了什么以及如何在这两种情况下获得完全相同的行为:一个和两个 shells?

模块的代码(这里有两个警告,但无论如何它都能编译,我试图仅在从完成分配的同一 Pid 调用时才允许释放):

-module(frequency).
-export([start/0, stop/0, allocate/0, deallocate/1]).
-export([init/0]).

%% These are the start functions used to crate and
%% initialize the server.

start() ->
  register(frequency, spawn(frequency, init, [])).

init() ->
  Frequencies = {get_frequencies(), []},
  loop(Frequencies).

% Hard Coded
get_frequencies() -> [10, 11, 12, 13, 14, 15].

%% The client Functions

stop()          -> call(stop).
allocate()      -> call(allocate).
deallocate(Freq)-> io:format("Calling deallocate~n",[]), call({deallocate, Freq}).

%% We hide all message passing and the message
%% protocol in a functional interface.

call(Message) ->
  Self = self(),
  io:format("Self: ~w~n", [Self]),
  frequency ! {request, Self, Message},
  receive
    {reply, Reply} -> Reply
  end.

%% The Main Loop

loop(Frequencies) ->
  receive 
    {request, Pid, allocate} ->
      {NewFrequencies, Reply} = allocate(Frequencies, Pid),
      reply(Pid, Reply),
      loop(NewFrequencies);
    {request, Pid2, {deallocate, Freq}} ->
      io:format("Dealocate ~w from pid ~w~n", [Freq, Pid2]),
      NewFrequencies = deallocate(Frequencies, Freq), %, Pid2),
      reply(Pid2, ok),
      loop(NewFrequencies);
    {request, Pid, stop} ->
      reply(Pid, ok)
  end.

reply(Pid, Reply) ->
  Pid ! {reply, Reply}.

%% The Internal Help Functions used to allocate and
%% deallocate frequencies.

allocate({[], Allocated}, _Pid) ->
  {{[], Allocated}, {error, no_frequency}};
allocate({[Freq|Free], Allocated}, Pid) ->
  {{Free, [{Freq, Pid}| Allocated]}, {ok, Freq}}.

deallocate({Free, Allocated}, Freq) -> %, Pid) ->
  Response = lists:keysearch(Freq, 1, Allocated),
  io:format("Response: ~w~n", [Response]),
  case Response of
    {value, {Freq, OPid}} ->
        case OPid of
          Pid ->
            NewAllocated = lists:keydelete(Freq, 1, Allocated),
            io:format("Removed freq~n",[]),
            {[Freq|Free], NewAllocated};
          _OtherPid ->
            io:format("Not allowed to remove freq~n",[]),
            {Free, Allocated}
        end;
    _ -> io:format("Not removed freq~n",[]), {Free, Allocated}
  end.

我没有对问题的完整解释,但它与 io:format 的用法有关,有一个模糊的(对我来说模糊 :o)组长的概念,用于确定shell 和 io:format 应该显示一条消息。

一般来说,在服务器进程中执行的服务器循环中显示消息不是一个好主意,最好在调用进程中执行的接口函数中执行。

我稍微修改了你的代码,删除了无用的打印并更正了 pid 的测试,它在多个 shells 中工作正常。

-module(freq).
-export([start/0, stop/0, allocate/0, deallocate/1]).
-export([init/0]).

%% These are the start functions used to crate and
%% initialize the server.

start() ->
  register(frequency, spawn(freq, init, [])).

init() ->
  Frequencies = {get_frequencies(), []},
  loop(Frequencies).

% Hard Coded
get_frequencies() -> [10, 11, 12, 13, 14, 15].

%% The client Functions

stop()          -> call(stop).
allocate()      -> call(allocate).
deallocate(Freq)-> io:format("Calling deallocate~n",[]), call({deallocate, Freq}).

%% We hide all message passing and the message
%% protocol in a functional interface.

call(Message) ->
  Self = self(),
  frequency ! {request, Self, Message},
  receive
    {reply, Reply} -> Reply
  end.

%% The Main Loop

loop(Frequencies) ->
  receive 
    {request, Pid, allocate} ->
      {NewFrequencies, Reply} = allocate(Frequencies, Pid),
      reply(Pid, Reply),
      loop(NewFrequencies);
    {request, Pid, {deallocate, Freq}} ->
      {NewFrequencies,Reply} = deallocate(Frequencies, Freq, Pid),
      reply(Pid, Reply),
      loop(NewFrequencies);
    {request, Pid, stop} ->
      reply(Pid, ok)
  end.

reply(Pid, Reply) ->
  Pid ! {reply, Reply}.

%% The Internal Help Functions used to allocate and
%% deallocate frequencies.

allocate({[], Allocated}, _Pid) ->
  {{[], Allocated}, {error, no_frequency}};
allocate({[Freq|Free], Allocated}, Pid) ->
  {{Free, [{Freq, Pid}| Allocated]}, {ok, Freq}}.

deallocate({Free, Allocated}, Freq, Pid) ->
  Response = lists:keysearch(Freq, 1, Allocated),
  case Response of
    {value, {Freq, Pid}} ->
      NewAllocated = lists:keydelete(Freq, 1, Allocated),
      {{[Freq|Free], NewAllocated},removed};
    {value, {Freq, _OtherPid}} ->
      {{Free, Allocated},not_owner};
    _ ->
      {{Free, Allocated},not_found}
  end.