为什么EventMachine比Ruby Thread更慢?

all*_*wei 10 ruby eventmachine

我有两个使用Mechanize来获取Google索引页面的脚本.我假设EventMachine比Ruby线程更快,但事实并非如此.

EventMachine代码费用: "0.24s user 0.08s system 2% cpu 12.682 total"

Ruby线程代码成本: "0.22s user 0.08s system 5% cpu 5.167 total "

我是以错误的方式使用EventMachine吗?

EventMachine的:

require 'rubygems'
require 'mechanize'
require 'eventmachine'

trap("INT") {EM.stop}

EM.run do 
  num = 0
  operation = proc {
    agent = Mechanize.new
    sleep 1
    agent.get("http://google.com").body.to_s.size
  }
  callback = proc { |result|
    sleep 1
    puts result
    num+=1
    EM.stop if num == 9
  }

  10.times do 
    EventMachine.defer operation, callback
  end
end
Run Code Online (Sandbox Code Playgroud)

Ruby线程:

require 'rubygems'
require 'mechanize'


threads = []
10.times do 
  threads << Thread.new do 
    agent = Mechanize.new
    sleep 1
    puts agent.get("http://google.com").body.to_s.size
    sleep 1
  end
end


threads.each do |aThread| 
  aThread.join
end
Run Code Online (Sandbox Code Playgroud)

Ben*_*nns 24

此线程中的所有答案都缺少一个关键点:您的回调正在反应器线程内运行,而不是在单独的延迟线程中运行.在defer调用中运行Mechanize请求是阻止循环的正确方法,但您必须小心,您的回调不会阻止循环.

当你运行时EM.defer operation, callback,操作在一个Ruby生成的线程中运行,该线程完成工作,然后在主循环内发出回调.因此,sleep 1operation并行运行,但回调运行顺序.这解释了运行时间差异接近9秒.

这是您正在运行的代码的简化版本.

EM.run {
  times = 0

  work = proc { sleep 1 }

  callback = proc {
    sleep 1
    EM.stop if (times += 1) >= 10
  }

  10.times { EM.defer work, callback }
}
Run Code Online (Sandbox Code Playgroud)

这需要大约12秒,并行睡眠为1秒,串行睡眠为10秒,开销为1秒.

要并行运行回调代码,您必须使用代理回调为它生成新线程,使用EM.defer如下:

EM.run {
  times = 0

  work = proc { sleep 1 }

  callback = proc {
    sleep 1
    EM.stop if (times += 1) >= 10
  }

  proxy_callback = proc { EM.defer callback }

  10.times { EM.defer work, proxy_callback }
}
Run Code Online (Sandbox Code Playgroud)

但是,如果您的回调被认为是在事件循环中执行代码,则可能会遇到此问题,因为它在一个单独的延迟线程中运行.如果发生这种情况,请将问题代码移动到proxy_callback proc的回调中.

EM.run {
  times = 0

  work = proc { sleep 1 }

  callback = proc {
    sleep 1
    EM.stop_event_loop if (times += 1) >= 5
  }

  proxy_callback = proc { EM.defer callback, proc { "do_eventmachine_stuff" } }

  10.times { EM.defer work, proxy_callback }
}
Run Code Online (Sandbox Code Playgroud)

该版本在大约3秒内运行,其占并行休眠的1秒,并行休眠1秒和开销1秒.


Ben*_*hes 9

是的,你错了.EventMachine通过进行立即返回的异步IO调用并在完成后通知"reactor"(由EM.run启动的事件循环)来工作.你有两个阻塞调用,打破了系统的目的,sleep和Mechanize.get.您必须使用特殊的异步/非阻塞库来从EventMachine派生任何值.

  • 你说他提出的例子可以用异步http库重写你是对的,但#defer方法的要点是具体的,所以你可以产生一个新的线程,它可以在不影响反应器运行循环的情况下进行阻塞操作.所以从理论上讲,他的例子并没有阻止运行循环.我对时差的猜测是如何安排线程. (2认同)

小智 7

你应该使用像em-http-request http://github.com/igrigorik/em-http-request这样的东西