带有多个客户端shell的Erlang - 生成的进程在“io”模块

时间:2017-09-22 23:41:12

标签: erlang erlang-shell

通过Joe的书,坚持第12章练习1.该练习要求人们编写一个函数 start(AnAtom,Fun),它将 AnAtom 注册为菌种(FUN)。我决定尝试一些看起来更容易的东西 - 拿完章节的'area_server'模块,然后修改它的start / 0函数:

start() ->
    Pid = spawn(ex1, loop, []),
    io:format("Spawned ~p~n",[Pid]),
    register(area, Pid).

所以代替执行任意Fun的进程,我正在注册'loop',这是area_server模块中执行所有工作的函数:

loop() ->
    receive
        {From, {rectangle, Width, Ht}} ->
            io:format("Computing for rectangle...~n"),
            From ! {self(), Width*Ht},
            loop();
        {From, {square, Side}} ->
            io:format("Computing for square...~n"),
            From ! {self(), Side*Side},
            loop();
        {From, Other} ->
            io:format("lolwut?~n"),
            From ! {self(), {error, Other}},
            loop()
    end.

似乎工作正常:

1> c("ex1.erl").
{ok,ex1}
2> ex1:start(). 
Spawned <0.68.0>
true
3> 
3> area ! {self(), hi}.
lolwut?
{<0.61.0>,hi}
4> flush().
Shell got {<0.68.0>,{error,hi}}
ok
5> area ! {self(), {square, 7}}.
Computing for square...
{<0.61.0>,{square,7}}
6> flush().
Shell got {<0.68.0>,49}
ok

当我试图测试多个进程可以与已注册的“服务器”通信时,事情变得很糟糕。 (CTRL-G,s,c 2)

我在一个新的shell中,与第一个一起运行 - 但是当我从这个新shell发送消息到我的'area'注册过程时,发生了一些令人讨厌的事情 - 当查询process_info(whereis(area))时,进程离开这个状态:

 {current_function,{ex1,loop,0}},
 {initial_call,{ex1,loop,0}},

到这一个:

 {current_function,{io,execute_request,2}},
 {initial_call,{ex1,loop,0}},

当消息队列开始增长时,消息未被处理。挂在模块 io 中,呵呵!在io操作上有什么东西被阻止了?显然,这个过程从我的ex1:loop / 0移到了io:execute_request / 2(无论是什么)......是我的傻打印导致了这个问题吗?

1 个答案:

答案 0 :(得分:1)

您的流程正在按照您的期望执行,但处理谁在何时控制STDOUT 除外。是的,这可能会导致shell中出现奇怪的表现行为。

所以让我们尝试类似这样的,不用任何暗示去STDOUT的IO命令,看看会发生什么。下面是一个shell会话,我定义了一个累积消息的循环,直到我要求它向我发送它已经积累的消息。我们可以从这个例子中看到(这个例子不会挂在允许与单个输出资源对话的人身上)进程按预期运行。

需要注意的一点是你不需要多个shell来与多个进程交谈

注意shell中flush/0的返回值 - 它是一个特殊的shell命令,它将shell的邮箱转储到STDOUT。

Eshell V9.0  (abort with ^G)
1> Loop = 
1>   fun L(History) ->
1>     receive
1>       halt ->
1>         exit(normal);
1>       {Sender, history} ->
1>         Sender ! History,
1>         L([]);
1>       Message ->
1>         NewHistory = [Message | History],
1>         L(NewHistory)
1>     end
1>   end.
#Fun<erl_eval.30.87737649>
2> {Pid1, Ref1} = spawn_monitor(fun() -> Loop([]) end).
{<0.64.0>,#Ref<0.1663562856.2369257474.102541>}
3> {Pid2, Ref2} = spawn_monitor(fun() -> Loop([]) end).
{<0.66.0>,#Ref<0.1663562856.2369257474.102546>}
4> Pid1 ! "blah".
"blah"
5> Pid1 ! "blee".
"blee"
6> Pid1 ! {self(), history}.
{<0.61.0>,history}
7> flush().
Shell got ["blee","blah"]
ok
8> Pid1 ! "Message from shell 1". 
"Message from shell 1"
9> Pid2 ! "Message from shell 1".
"Message from shell 1"
10> 
User switch command
 --> s
 --> j
   1  {shell,start,[init]}
   2* {shell,start,[]}
 --> c 2
Eshell V9.0  (abort with ^G)
1> Shell1_Pid1 = pid(0,64,0).
<0.64.0>
2> Shell1_Pid2 = pid(0,66,0).
<0.66.0>
3> Shell1_Pid1 ! "Message from shell 2".
"Message from shell 2"
4> Shell1_Pid2 ! "Another message from shell 2".
"Another message from shell 2"
5> Shell1_Pid1 ! {self(), history}.
{<0.77.0>,history}
6> flush().
Shell got ["Message from shell 2","Message from shell 1"]
ok
7> 
User switch command
 --> c 1

11> Pid2 ! {self(), history}.
{<0.61.0>,history}
12> flush().
Shell got ["Another message from shell 2","Message from shell 1"]
ok