并发线程的并行进度输出
Parallel progress output from concurrent threads
有这个代码:
workers = (1..3).map do |n|
Thread.new do
puts
print "Worker_#{n}..."
sleep rand
print "done (#{n})"
end
end.each &:join
puts
puts '- ready -'
我们怎样才能得到这样的正确输出(行动态更新):
Worker_1...done (1)
Worker_2...done (2)
Worker_3...done (3)
- ready -
而不是这样:
Worker_1...
Worker_2...
Worker_3...done (3)done (1)done (2)
- ready -
高级选项:如果我们将 stdout 重定向到文件——它应该看起来也不错。
我会介绍一些 class 来管理输出。它还会将回车移动到上方 3 行以更新每次输出(在 print
方法)。
class StatusPrint
def initialize
@hash = {}
end
def declare_worker(name)
@hash[name] = "Worker_#{name}"
end
def complete_worker(name)
@hash[name] += "...done (#{name})"
end
def print(move_carriage: true)
puts "3[#{@hash.size + 1}A" if move_carriage
puts @hash.values.join("\n")
end
def done!
@done = true
end
def done?
@done
end
end
因此您的代码将如下所示
status_print = StatusPrint.new
workers = (1..3).map do |n|
Thread.new do
status_print.declare_worker(n)
sleep rand
status_print.complete_worker(n)
end
end
print_loop = Thread.new do
status_print.print(move_carriage: false)
until status_print.done?
sleep 0.1
status_print.print
end
end
workers.each &:join
status_print.done!
print_loop.join
puts '- ready -'
如果您想通过管道传输输出,它也不会起作用,因此您必须添加一些命令行选项来禁用更新输出并打印此静态文本:
Worker_1...done (1)
Worker_2...done (2)
Worker_3...done (3)
- ready -
有关更新输出的更多信息,另请参阅 https://blog.stevenocchipinti.com/2013/06/removing-previously-printed-lines.html/。
最终,我创建了一个 gem 来解决这个问题:
ParaLines Ruby gem
输出样本:
一些最小的使用示例如下所示:
require 'paralines'
plines = ParaLines.new
plines.add_static_line 'Gems latest versions'
plines.add_empty_line
gems = %w[sinatra rspec paralines rails hanami]
plines.add_static_line '- Random order:'
threads = gems.map do |name|
Thread.new do
plines << name.ljust(15, '.')
res = `gem list -r #{name} -e`
plines << 'v' + res[/\((.+)\)/, 1]
end
end
sleep 0.2
plines.add_empty_line
plines.add_static_line '- Sorted:'
threads += gems.sort.map do |name|
sline = plines.add_shared_line name.ljust(15, '.')
Thread.new do
res = `gem list -r #{name} -e`
sline << 'v' + res[/\((.+)\)/, 1]
end
end
threads.each &:join
上找到带有 GIF 动画输出的详细示例的源代码
有这个代码:
workers = (1..3).map do |n|
Thread.new do
puts
print "Worker_#{n}..."
sleep rand
print "done (#{n})"
end
end.each &:join
puts
puts '- ready -'
我们怎样才能得到这样的正确输出(行动态更新):
Worker_1...done (1)
Worker_2...done (2)
Worker_3...done (3)
- ready -
而不是这样:
Worker_1...
Worker_2...
Worker_3...done (3)done (1)done (2)
- ready -
高级选项:如果我们将 stdout 重定向到文件——它应该看起来也不错。
我会介绍一些 class 来管理输出。它还会将回车移动到上方 3 行以更新每次输出(在 print
方法)。
class StatusPrint
def initialize
@hash = {}
end
def declare_worker(name)
@hash[name] = "Worker_#{name}"
end
def complete_worker(name)
@hash[name] += "...done (#{name})"
end
def print(move_carriage: true)
puts "3[#{@hash.size + 1}A" if move_carriage
puts @hash.values.join("\n")
end
def done!
@done = true
end
def done?
@done
end
end
因此您的代码将如下所示
status_print = StatusPrint.new
workers = (1..3).map do |n|
Thread.new do
status_print.declare_worker(n)
sleep rand
status_print.complete_worker(n)
end
end
print_loop = Thread.new do
status_print.print(move_carriage: false)
until status_print.done?
sleep 0.1
status_print.print
end
end
workers.each &:join
status_print.done!
print_loop.join
puts '- ready -'
如果您想通过管道传输输出,它也不会起作用,因此您必须添加一些命令行选项来禁用更新输出并打印此静态文本:
Worker_1...done (1)
Worker_2...done (2)
Worker_3...done (3)
- ready -
有关更新输出的更多信息,另请参阅 https://blog.stevenocchipinti.com/2013/06/removing-previously-printed-lines.html/。
最终,我创建了一个 gem 来解决这个问题:
ParaLines Ruby gem
输出样本:
一些最小的使用示例如下所示:
require 'paralines'
plines = ParaLines.new
plines.add_static_line 'Gems latest versions'
plines.add_empty_line
gems = %w[sinatra rspec paralines rails hanami]
plines.add_static_line '- Random order:'
threads = gems.map do |name|
Thread.new do
plines << name.ljust(15, '.')
res = `gem list -r #{name} -e`
plines << 'v' + res[/\((.+)\)/, 1]
end
end
sleep 0.2
plines.add_empty_line
plines.add_static_line '- Sorted:'
threads += gems.sort.map do |name|
sline = plines.add_shared_line name.ljust(15, '.')
Thread.new do
res = `gem list -r #{name} -e`
sline << 'v' + res[/\((.+)\)/, 1]
end
end
threads.each &:join
上找到带有 GIF 动画输出的详细示例的源代码