问题
I am trying to figure out how to make use of deferrables when it comes to long running computations that I have to implement on my own. For my example I want to calculate the first 200000 Fibonacci numbers but return only a certain one.
My first attempt of a deferrable looked like so:
class FibA
include EM::Deferrable
def calc m, n
fibs = [0,1]
i = 0
do_work = proc{
puts "Deferred Thread: #{Thread.current}"
if i < m
fibs.push(fibs[-1] + fibs[-2])
i += 1
EM.next_tick &do_work
else
self.succeed fibs[n]
end
}
EM.next_tick &do_work
end
end
EM.run do
puts "Main Thread: #{Thread.current}"
puts "#{Time.now.to_i}\n"
EM.add_periodic_timer(1) do
puts "#{Time.now.to_i}\n"
end
# calculating in reactor thread
fib_a = FibA.new
fib_a.callback do |x|
puts "A - Result: #{x}"
EM.stop
end
fib_a.calc(150000, 21)
end
Only to realize that everything seemed to work pretty well, but the thread the deferrable runs in is the same as the reactor thread (knowing that everything runs inside one system thread unless rbx or jruby are used). So I came up with a second attempt that seems nicer to me, especially because of different callback binding mechanism and the use of different threads.
class FibB
include EM::Deferrable
def initialize
@callbacks = []
end
def calc m, n
work = Proc.new do
puts "Deferred Thread: #{Thread.current}"
@fibs = 1.upto(m).inject([0,1]){ |a, v| a.push(a[-1]+a[-2]); a }
end
done = Proc.new do
@callbacks.each{ |cb| cb.call @fibs[n]}
end
EM.defer work, done
end
def on_done &cb
@callbacks << cb
end
end
EM.run do
puts "Main Thread: #{Thread.current}"
puts "#{Time.now.to_i}\n"
EM.add_periodic_timer(1) do
puts "#{Time.now.to_i}\n"
end
# calculating in external thread
fib_b = FibB.new
fib_b.on_done do |res|
puts "B - Result: #{res}"
end
fib_b.on_done do
EM.stop
end
fib_b.calc(150000, 22)
end
Which one is the implementation that I should prefer? Are both wrong? Is there another, a better one?
Even more interesting: Is the second attempts a perfect way to implement whatever I want (except I/O op's) without blocking the reactor?
回答1:
Definitely EM.defer (or Thread.new I suppose), doing a long-running calculation in EM.next_tick will block your reactor for other things.
As a general rule, you don't want ANY block running inside reactor to be running for long regardless if it is or isn't IO blocking or the entire app halts while this is happening.
来源:https://stackoverflow.com/questions/20074518/better-use-em-next-tick-or-em-defer-for-long-running-calculation-with-eventmachi