From: Robert Klemme Date: 2009-12-17T16:30:07+09:00 Subject: Re: Using threads to show progress On 16.12.2009 16:09, Aldric Giacomoni wrote: > Brian Candler wrote: >> Killing threads asynchronously is usually a Bad Idea[TM]. It leaves you >> open to all sorts of race conditions. Terminating gracefully, by sending >> a nil into the queue as I showed before, is much better. > I read the rdocs for the Queue class and now I understand how it works - > you are right, I will fix my ugly code. >> The main downside of this is if you are processing 10,000,000 things and >> you don't want 10,000,000 screen updates. In that case, I suggest you >> keep a "last output" timestamp and only update once per second. > That is a good idea! >> Another option might be to take advantage of duck-typing. Replace >> $stderr with an object of your own which looks like an IO, but which >> only writes the data if no update has been seen for the last second, or >> something like that. > > That sounds like work. *shudder* > On the other hand, that's a great way to start playing with IO objects. IMHO it is not necessary though. What you basically only need is to execute an action only if a particular condition becomes true. That is not specific to IO objects - you don't even need to implement the full protocol of IO which indeed sounds like work. A simpler solution looks like this: 1. high notification and change frequency => time based class TimeThrottle def initialize(interval, &act) @interval = interval @last = Time.at 0 @act = act end def update(*a, &act) n = Time.now if n - @last > @interval @last = n (act || @act)[*a] end end end 2. high notification and low change frequency => change based class ChangeThrottle def initialize(&act) @last = nil @act = act end def update(*a, &act) if a != @last (act || @act)[*a] @last = a end end end Now we can combine that in an example with three different update models: def dump(label, msg) printf "%-30s %s\n", label[0, 30], msg end tt = TimeThrottle.new 1 do |x| dump "time", x end ct = ChangeThrottle.new do |x| dump "change", x * 100_000 end ct2 = ChangeThrottle.new do |x| dump "combined", x * 100 end co = TimeThrottle.new 1 do |x| ct2.update x end tt.update { dump "time", "started" } ct.update { dump "change", "started" } 1_000_000.times do |i| tt.update i ct.update i / 100_000 # reduce change frequency co.update i / 100 end tt.update { dump "time", "stopped" } ct.update { dump "change", "stopped" } Kind regards robert -- remember.guy do |as, often| as.you_can - without end http://blog.rubybestpractices.com/