如何在不阻塞反应器的情况下在EventMachine中运行同步长时间运行的操作?

时间:2014-02-11 15:48:05

标签: ruby eventmachine fibers

我想以指定的顺序运行一系列Proc s(即,它们不能异步运行)。其中一些可能会花费任意长的时间。

我的代码在EventMachine反应器的上下文中运行。 是否有一种已知的习惯用于编写这种代码而不用阻塞主反应堆?

2 个答案:

答案 0 :(得分:3)

正如@maniacalrobot所说,使用EM.defer/deferrable可以在不阻塞反应堆的情况下运行过程。 但是当你需要连续运行几个过程时,你会进入“回调地狱”。

我知道两种解决方案可以让代码更具可读性:承诺和光纤。

Promises 为您提供了一个很好的API来组成异步调用,那里有很多好文章,包括:

Fibers 是一种更具特色的ruby工具,它使您的代码在执行异步操作时看起来是同步的。

这是一个辅助方法,用于异步执行proc(延迟)但仍然阻塞调用代码而不阻塞主反应器(这是Fibers的神奇之处):

def deferring(action)
  f = Fiber.current

  safe_action = proc do
    begin
      res = action.call
      [nil, res]
    rescue => e
      [e, nil]
    end
  end

  EM::defer(safe_action, proc { |error, result| f.resume([error, result]) })

  error, result = Fiber.yield

  raise error if error

  result
end

用法示例:

action1_res = deferring(proc do 
  puts 'Async action 1'
  42
end

begin
  deferring(proc do
    puts "Action1 answered #{action1_res}"
    raise 'action2 failed'
  end)
rescue => error
  puts error
end

答案 1 :(得分:0)

通常会阻止主反应堆循环的任何代码都应使用EM#defer运行。 EM#defer需要两个块作为参数,第一个块在不同的线程中运行,不应该阻塞反应器。可以传递第二个可选块,当第一个块完成时它将被调用(它也将接收第一个块的结果)。

进一步阅读https://github.com/eventmachine/eventmachine/wiki/EM::Deferrable-and-EM.defer

链接2个长时间运行操作的示例如下所示:

logic_block = Proc.new { long_running_operation }
callback = Proc.new { |long_running_operation_result| EM.defer next_long_running_operation }

EM.defer logic_block, callback

请注意,第二个(回调)块在reactor循环上运行,因此如果您计划将多个长时间运行的代码链接在一起,则需要在回调内再次调用EM.defer。