erlang - 生成过程中的 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>
有人可以解释第一个示例中发生了什么竞争条件吗?
解决方案
实际上,这不完全是比赛条件。问题是因为我们只有一个 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>
推荐阅读
- java - 将 JavaFX jmods 添加到 java --list-modules
- c++ - Qtconcurrent - 从 gui 线程启动第二个线程的简单方法
- java - 添加此 servlet 代码服务器时停止
- c++ - 使用反向迭代器的中点
- tcl - 从 tcl 中的给定路径中删除大括号
- javascript - 尽管被捕获,但在生成器中抛出的错误完成了它
- java - FirebaseRecyclerAdapter DatabaseException:无法将 java.lang.String 类型的对象转换为 Posts 类型
- user-interface - 是否有现成的 UI 可以在 ASP.NET 中编辑数据库表?
- javascript - Simple React Native iOS App 未在 xcode 和命令行中运行
- delphi - 如何在匿名方法中捕获变量?