'Parallel progress output from concurrent threads
Having this code:
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 -'
how can we get a correct output like this (lines updated dynamically):
Worker_1...done (1)
Worker_2...done (2)
Worker_3...done (3)
- ready -
and not like this:
Worker_1...
Worker_2...
Worker_3...done (3)done (1)done (2)
- ready -
Advanced option: if we redirect stdout to file — it should look fine too.
Solution 1:[1]
I'd introduce some class to manage output. It will also move the carriage 3 lines above to update the output every time (at the print method).
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 "\033[#{@hash.size + 1}A" if move_carriage
puts @hash.values.join("\n")
end
def done!
@done = true
end
def done?
@done
end
end
So your code will look like this
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 -'
Also it won't work if you want to pipe your output so you'll have to add some command line option to disable updating the output and print this static text:
Worker_1...done (1)
Worker_2...done (2)
Worker_3...done (3)
- ready -
Also see https://blog.stevenocchipinti.com/2013/06/removing-previously-printed-lines.html/ for more about updating the output.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Dmitry Barskov |
