我有两个使用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
Ruby Thread:
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
答案 0 :(得分:24)
此主题中的所有答案都缺少一个关键点:您的回调正在反应器线程内运行,而不是在单独的延迟线程中运行。在defer
调用中运行Mechanize请求是阻止循环的正确方法,但您必须小心,您的回调不会阻止循环。
运行EM.defer operation, callback
时,操作在Ruby生成的线程内运行,该线程完成工作,然后在主循环内发出回调。因此,sleep 1
中的operation
并行运行,但回调串行。这解释了运行时间差异接近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 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 }
}
该版本在大约3秒钟内运行,占并行休眠1秒,并行回拨1秒,开销1秒。
答案 1 :(得分:9)
答案 2 :(得分:7)
您应该使用类似em-http-request http://github.com/igrigorik/em-http-request
的内容答案 3 :(得分:2)
EventMachine“defer”实际上是从它设法处理您的请求的线程池中生成Ruby线程。是的,EventMachine是为非阻塞IO操作而设计的,但是defer命令是一个例外 - 它旨在允许您在不阻塞反应器的情况下执行长时间运行的操作。
所以,它会比裸线程慢一点,因为实际上它只是用EventMachine的线程池管理器的开销来启动线程。
您可以在此处详细了解:http://eventmachine.rubyforge.org/EventMachine.html#M000486
也就是说,抓取页面是一个很好的使用EventMachine,但正如其他海报所说,你需要使用一个非阻塞的IO库,然后使用next_tick或类似的来启动你的任务,而不是推迟,哪个打破你的任务离开反应堆循环。