我想以一种非阻塞(异步)的方式从Ruby运行多个耗时的shell命令。
我希望将选项传递给命令,接收Ruby中的输出,(理想情况下)处理错误。
下面的脚本自然需要15秒才能执行:
test.rb
3.times do |i|
puts `sleep 5; echo #{i} | tail -n 1` # some time-consuming complex command
end$ /usr/bin/time ruby test.rb
0
1
2
15.29 real 0.13 user 0.09 sys使用线程,它显然可以并行执行,它只需5秒,就像预期的那样:
threads = []
3.times do |i|
threads << Thread.new {
puts `sleep 5; echo #{i} | tail -n 1`
}
end
threads.each {|t| t.join() }$ /usr/bin/time ruby test.rb
2
0
1
5.17 real 0.12 user 0.06 sys但这是最好的方法吗?还有别的办法吗?
我也使用Open3.popen2编写了文章,但这似乎需要15秒来执行,如第一个示例所示(除非封装在线程中):
require 'open3'
3.times do |i|
Open3.popen2("sleep 5; echo #{i} | tail -n 1") do |stdin, stdout|
puts stdout.read()
end
end文献资料描述了“块表单”和“非块表单”,但是这个“块”指的是匿名函数,与并发性无关,对吗?
只有Open3类才能阻止执行吗?
发布于 2022-03-10 10:17:14
代码的问题是stdout.read是一个阻塞调用。
您可以将读取推迟到命令完成为止。
首先,创建以下命令:
commands = Array.new(3) { |i| Open3.popen2("sleep 5; echo hello from #{i}") }然后,等待每个命令完成:
commands.each { |stdin, stdout, wait_thr| wait_thr.join }最后,收集输出并关闭IO流:
commands.each do |stdin, stdout, wait_thr|
puts stdout.read
stdin.close
stdout.close
end输出:(5秒后)
hello from 0
hello from 1
hello from 2https://stackoverflow.com/questions/71420764
复制相似问题