最好使用EM.next_tick或EM.defer与Eventmachine进行长时间运行计算?

时间:2013-11-19 14:44:17

标签: ruby multithreading eventmachine asynchronous

我正在试图弄清楚如何在我必须自己实现的长时间运行的计算中使用延迟。对于我的例子,我想计算前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操作除外)而不会阻塞反应堆?

1 个答案:

答案 0 :(得分:3)

绝对是EM.defer(或者我认为是Thread.new),在EM.next_tick中进行长时间运行的计算会阻止你的反应堆进行其他操作。

作为一般规则,你不希望在反应堆内运行的任何块长时间运行,无论它是否阻塞或整个应用程序在发生这种情况时停止运行。