我正在试图弄清楚如何在我必须自己实现的长时间运行的计算中使用延迟。对于我的例子,我想计算前200,000个斐波纳契数,但只返回一个。
我对延期的第一次尝试看起来像是这样:
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
我应该选择哪一个实现?都错了吗?还有另一个更好的吗?
更有趣的是:第二次尝试是否是一种完美的方式来实现我想要的任何东西(I / O操作除外)而不会阻塞反应堆?
答案 0 :(得分:3)
绝对是EM.defer(或者我认为是Thread.new),在EM.next_tick中进行长时间运行的计算会阻止你的反应堆进行其他操作。
作为一般规则,你不希望在反应堆内运行的任何块长时间运行,无论它是否阻塞或整个应用程序在发生这种情况时停止运行。