Лучше использовать EM.next_tick или EM.defer для долгосрочных расчетов с Eventmachine?
Я пытаюсь понять, как использовать отсрочку, когда дело доходит до длительных вычислений, которые я должен реализовать самостоятельно. Для моего примера я хочу вычислить первые 200000 чисел Фибоначчи, но вернуть только определенное.
Моя первая попытка отсрочки выглядела так:
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
Только для того, чтобы понять, что все, кажется, работает довольно хорошо, но поток, в котором работает отложенный, такой же, как поток реактора (зная, что все работает в одном системном потоке, если не используются rbx или jruby). Поэтому я придумал вторую попытку, которая мне кажется более приятной, особенно из-за разного механизма связывания обратных вызовов и использования разных потоков.
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