背景&目标
开发一个程序交互接口:
- 可使用Julia异步、后台启动一个命令/exe文件
- 支持在运行时向程序异步写入命令、读取输出(文本IO)
- 支持调用函数关闭程序,关闭程序进程
- 支持对多个进程的并行、异步调度(e.g. 同时启动多个cmd shell,用户可以指定「向哪一个cmd shell读写输入/输出」)
问题
在「读取输出」的过程中,遇到「read没有输出导致主进程死循环阻塞」的问题
- 函数
read
/readline
/readlines
均无法识别「输出是否完整」 - 主进程阻塞后,除非使用
Ctrl+C
强行停止,代码无法自行执行完毕,后续对程序的IO操作无法继续执行
MWE
proc = open(`cmd`, write=true, read=true) # 后台异步打开一个cmd进程
println(proc, "echo 1") # 使用println输入指令
@show readline(proc) # 输出「"readline(proc) = 【cmd路径】> echo 1"」
@show readline(proc) # 输出「"readline(proc) = 1"」
@show readline(proc) # REPL/Debugger/.jl脚本 无响应:主进程阻塞,程序卡死
# 或:使用循环从指令中获取输入(及其应答)
for l in readlines(proc)
@show l
end
# 此后主进程阻塞,程序卡死 #
预期:执行前eof(proc) == true、执行时报错 或 执行后返回空值
实际:主进程阻塞,无法继续执行代码
在REPL运行上述代码直至卡顿后,使用Ctrl+C强制关闭的报错信息:
ERROR: InterruptException:
Stacktrace:
[1] poptask(W::Base.IntrusiveLinkedListSynchronized{Task})
@ Base .\task.jl:974
[2] wait()
@ Base .\task.jl:983
[3] wait(c::Base.GenericCondition{Base.Threads.SpinLock}; first::Bool)
@ Base .\condition.jl:130
[4] wait
@ .\condition.jl:125 [inlined]
[5] readuntil(x::Base.PipeEndpoint, c::UInt8; keep::Bool)
@ Base .\stream.jl:1014
[6] readuntil(io::Base.Process, arg::UInt8; kw::Base.Pairs{Symbol, Bool, Tuple{Symbol}, NamedTuple{(:keep,), Tuple{Bool}}})
@ Base .\io.jl:442
[7] readuntil
@ .\io.jl:442 [inlined]
[8] readline(s::Base.Process; keep::Bool)
@ Base .\io.jl:548
[9] readline(s::Base.Process)
@ Base .\io.jl:547
[10] top-level scope
@ REPL[28]:1
=#
已参考过的问题/话题
- 【中】请问Julia如何与外部程序互传数据?不是读写文件的那种
- 使用"r+"与设置「write=true, read=true」等价:无法解决问题
- 【英】Run commands on externally running process - General Usage - Julia Programming Language — 在外部运行进程上运行命令
- 运行
read(pstdout)
仍会遇到上述问题
- 运行
- 【英】Processing output from long running process - New to Julia - Julia Programming Language — 处理长时间运行的进程的输出
- 遍历
eachline
仍会遇到上述问题
- 遍历
- 【英】 How to continuously communicate with an external program? - General Usage - Julia Programming Language — 如何与外部程序持续通信?
- 命令加上前缀
stdbuf -oL
仍会遇到上述问题
- 命令加上前缀