10

我有两个使用 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?

事件机:

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

红宝石线程:

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
4

4 回答 4

25

该线程中的所有答案都缺少一个关键点:您的回调正在反应器线程中运行,而不是在单独的延迟线程中运行。在调用中运行 Mechanize 请求defer是避免阻塞循环的正确方法,但您必须注意您的回调不会同时阻塞循环。

当您运行时EM.defer operation, callback,该操作在 Ruby 生成的线程中运行,该线程完成工作,然后在主循环中发出回调。因此,sleep 1inoperation并行运行,而回调串行运行。这解释了运行时间接近 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 }
}

这大约需要 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 }
}

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

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 }
}

这个版本跑了大约 3 秒,其中并行操作休眠 1 秒,并行回调休眠1 秒,开销占用 1 秒。

于 2012-08-02T13:36:00.867 回答
9

Yep, you're using it wrong. EventMachine works by making asynchronous IO calls that return immediately and notify the "reactor" (the event loop started by EM.run) when they are completed. You have two blocking calls that defeat the purpose of the system, sleep and Mechanize.get. You have to use special asynchronous/non-blocking libraries to derive any value from EventMachine.

于 2010-06-18T01:40:29.420 回答
7

你应该使用类似 em-http-request http://github.com/igrigorik/em-http-request

于 2010-08-19T18:08:03.663 回答
2

EventMachine "defer" 实际上是从它管理处理您的请求的线程池中生成 Ruby 线程。是的,EventMachine 是为非阻塞 IO 操作而设计的,但 defer 命令是一个例外——它旨在允许您在不阻塞反应器的情况下进行长时间运行的操作。

所以,它会比裸线程慢一点,因为实际上它只是使用 EventMachine 的线程池管理器的开销来启动线程。

您可以在此处阅读有关延迟的更多信息:http: //eventmachine.rubyforge.org/EventMachine.html#M000486

也就是说,获取页面是 EventMachine 的一个很好的用途,但是正如其他发帖人所说,您需要使用非阻塞 IO 库,然后使用 next_tick 或类似的方法来启动您的任务,而不是使用 defer,这会使您的任务中断的反应器回路。

于 2012-01-14T01:09:17.863 回答