非阻塞反引号
Non-blocking backticks
我想 运行 以非阻塞(异步)方式从 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
有了Thread,显然可以并行执行,不出所料只需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
The documentation描述了“块形式”和“非块形式”,但是这个“块”是指匿名函数,与并发无关,对吗?
Open3 class 只能阻塞执行吗?
您的代码的问题是 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 2
我想 运行 以非阻塞(异步)方式从 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
有了Thread,显然可以并行执行,不出所料只需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
The documentation描述了“块形式”和“非块形式”,但是这个“块”是指匿名函数,与并发无关,对吗?
Open3 class 只能阻塞执行吗?
您的代码的问题是 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 2