首页 > 解决方案 > 生成过程中的 io:getline(Prompt):不显示提示

问题描述

这是代码:

-module(my).
-compile(export_all).

test() ->
    register(go, spawn(my, init, []) ).

init() ->
    Reply = io:get_line("enter:"),
    io:format("Reply= ~s~n", [Reply]).

在外壳中:

~/erlang_programs$ erl
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V9.3  (abort with ^G)

1> c(my).
my.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,my}

2> my:test().
true 

3>    

如果我在timer:sleep()这里添加:

test() ->
    register(go, spawn(my, init, []) ).

init() ->
    timer:sleep(1000),  %%<<<==========HERE
    Reply = io:get_line("enter:"),
    io:format("Reply= ~s~n", [Reply]).

并在 shell 中运行新代码,然后 1 秒后3>,第一个示例输出中的 shell 提示符突然消失并替换为enter:

~/erlang_programs$ erl
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V9.3  (abort with ^G)

1> c(my).    
my.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,my}

2> my:test().
true
enter:

如果我输入一些东西,那么正如预期的那样,我看到了Reply= xx

如果我移动timer:sleep()到这里:

test() ->
    register(go, spawn(my, init, []) ),
    timer:sleep(1000).  %%<<======HERE

init() ->
    Reply = io:get_line("enter:"),
    io:format("Reply= ~s~n", [Reply]).

我在 shell 中运行代码,我看到:

~/erlang_programs$ erl
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V9.3  (abort with ^G)

1> c(my).    
my.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,my}

2> my:test().
enter:

然后enter:突然消失并被替换为ok

1> c(my).    
my.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,my}

2> my:test().
ok    

3>    

确实按预期工作:

1> Reply = io:get_line("enter: ").
enter: hello
"hello\n"

2> Reply.
"hello\n"

3> 

有人可以解释第一个示例中发生了什么竞争条件吗?

标签: erlang

解决方案


实际上,这不完全是比赛条件。问题是因为我们只有一个 std_in 流并且在给定时间内只有一个进程可以使用它。

1) 在第一个代码片段过程中,“go”首先使用 get_line/1 函数控制 std_in。但是在 test/0 函数返回到 Erlang shell 之后,shell 在 std_in 下获得控制权(Erlang shell 正在等待用户输入提示,如“>”)。因此,进程“go”现在与 std_in 取消链接,并且不会从 std_in 接收任何字符。所有输入都进入 Erlang shell。

2) 第二个片段在 init/1 中有 sleep/1 功能。所以情况正好相反。我们允许 Erlang shell 到达等待输入状态并在“go”过程中执行 get_line/1 之后。所以现在'go'进程控制std_in并从键盘接收字符。但是在“go”进程终止后,Erlang shell 会恢复其状态。

3)第三个片段只是第一个但加重了 sleep/1 功能的情况。

4)没有解释第四个片段。它按预期工作。

5)为了说明这个观点,你可以运行下面的代码:

-module(my).
-compile(export_all).

test() ->
    spawn_link(my, init1, []),
    spawn_link(my, init2, []).

init1() ->
    timer:sleep(100),
    init(0, "0").

init2() ->
    timer:sleep(100),
    init(0, "1").

init(N, Title) ->
%    io:format(">>init(~p)~n", [N]),
    Reply = io:get_line(Title ++ "> enter:"),
    io:format("[~s] (e - exit) Reply= ~s", [Title, Reply]),
    case Reply of
      "e\n" -> ok;
      _ -> init(N+1, Title)
    end.

该模块使用 get_line/1 启动两个进程。但是最近只有一个进程可以排除来自 std_in 的输入:

50> my:test().
<0.192.0>
1> enter:w
[1] (e - exit) Reply= w
1> enter:d
[1] (e - exit) Reply= d
1> enter:e
[1] (e - exit) Reply= e
0> enter:dd
[0] (e - exit) Reply= dd
0> enter:e
[0] (e - exit) Reply= e
51> 

推荐阅读