代码之家  ›  专栏  ›  技术社区  ›  allenwei

为什么EventMachine的延迟比Ruby线程慢?

  •  10
  • allenwei  · 技术社区  · 16 年前

    我有两个脚本使用Mechanize获取Google索引页。我以为EventMachine会比Ruby线程快,但事实并非如此。

    事件机器代码成本: "0.24s user 0.08s system 2% cpu 12.682 total"

    "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 回复  |  直到 13 年前
        1
  •  9
  •   Ben Hughes    16 年前

    是的,你用错了。EventMachine的工作原理是发出异步IO调用,这些调用立即返回,并在完成时通知“reactor”(由EM.run启动的事件循环)。您有两个阻塞调用,它们破坏了系统的功能sleep和Mechanize.get。必须使用特殊的异步/非阻塞库从EventMachine派生任何值。

        2
  •  25
  •   Benjamin Manns    14 年前

    这个线程中的所有答案都缺少一个关键点:回调在reactor线程中运行,而不是在单独的延迟线程中运行。在中运行Mechanize请求 defer call是防止阻塞循环的正确方法,但是您必须小心回调不会同时阻塞循环。

    当你跑的时候 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 }
    }
    

    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秒钟的回调睡眠时间 平行 一秒钟的开销。

        4
  •  2
  •   Joshua    14 年前

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

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

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

    这就是说,获取页面是EventMachine的一个很好的用途,但是正如其他海报所说的,您需要使用一个非阻塞IO库,然后使用next\ tick或类似的工具来启动您的任务,而不是defer,这将使您的任务脱离reactor循环。

    推荐文章