ruby - 如果它们不是真正的并行,我能给 Ruby 线程什么用?

标签 ruby multithreading parallel-processing

当我第一次发现线程时,我尝试通过在多个线程中调用 sleep 来检查它们是否确实按预期工作,而不是正常调用 sleep。它奏效了,我很高兴。

但后来我的一个 friend 告诉我,这些线程并不是真正平行的, sleep 一定是假装的。

所以现在我写了这个测试来做一些真正的处理:

class Test
  ITERATIONS = 1000

  def run_threads
    start = Time.now

    t1 = Thread.new do
      do_iterations
    end

    t2 = Thread.new do
      do_iterations
    end

    t3 = Thread.new do
      do_iterations
    end

    t4 = Thread.new do
      do_iterations
    end

    t1.join
    t2.join
    t3.join
    t4.join

    puts Time.now - start
  end

  def run_normal
    start = Time.now

    do_iterations
    do_iterations
    do_iterations
    do_iterations

    puts Time.now - start
  end

  def do_iterations
    1.upto ITERATIONS do |i|
      999.downto(1).inject(:*) # 999!
    end
  end
end

现在我很难过,因为 run_threads() 不仅没有比 run_normal 表现得更好,它甚至更慢!

那么,如果线程不是真正并行的,我为什么要使我的应用程序复杂化呢?

** 更新 **

@fl00r 说如果我将线程用于 IO 任务,我可以利用线程,所以我又写了两个 do_iterations 的变体:

def do_iterations
  # filesystem IO
  1.upto ITERATIONS do |i|
    5.times do
      # create file
      content = "some content #{i}"
      file_name = "#{Rails.root}/tmp/do-iterations-#{UUIDTools::UUID.timestamp_create.hexdigest}"
      file = ::File.new file_name, 'w'
      file.write content
      file.close

      # read and delete file
      file = ::File.new file_name, 'r'
      content = file.read
      file.close
      ::File.delete file_name
    end
  end
end

def do_iterations
  # MongoDB IO (through MongoID)
  1.upto ITERATIONS do |i|
    TestModel.create! :name => "some-name-#{i}"
  end
  TestModel.delete_all
end

性能结果仍然相同:正常 > 线程。

但现在我不确定我的 VM 是否能够使用所有内核。当我测试完后会回来。

最佳答案

只有当你有一些慢 IO 时,线程才会更快。

在 Ruby 中你有全局解释器锁,所以一次只能有一个线程工作。因此,Ruby 花费很多时间来管理某个时刻应该触发哪个线程(线程调度)。所以在你的情况下,当没有任何 IO 时它会变慢!

您可以使用 Rubinius 或 JRuby 来使用真正的线程。

IO 示例:

module Test
  extend self

  def run_threads(method)
    start = Time.now

    threads = []
    4.times do
      threads << Thread.new{ send(method) }
    end

    threads.each(&:join)

    puts Time.now - start
  end

  def run_forks(method)
    start = Time.now

    4.times do
      fork do
        send(method)
      end
    end
    Process.waitall

    puts Time.now - start
  end

  def run_normal(method)
    start = Time.now

    4.times{ send(method) }

    puts Time.now - start
  end

  def do_io
    system "sleep 1"
  end

  def do_non_io
    1000.times do |i|
      999.downto(1).inject(:*) # 999!
    end
  end
end

Test.run_threads(:do_io)
#=> ~ 1 sec
Test.run_forks(:do_io)
#=> ~ 1 sec
Test.run_normal(:do_io)
#=> ~ 4 sec

Test.run_threads(:do_non_io)
#=> ~ 7.6 sec
Test.run_forks(:do_non_io)
#=> ~ 3.5 sec
Test.run_normal(:do_non_io)
#=> ~ 7.2 sec

IO 作业在线程和进程中的速度是线程和进程的 4 倍,而进程中的非 IO 作业的速度是线程和同步方法的两倍。

也在 Ruby 中呈现 Fibers轻量级的“corutines”和很棒的em-synchrony gem处理异步进程

关于ruby - 如果它们不是真正的并行,我能给 Ruby 线程什么用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10225838/

相关文章:

ruby - 在 Ruby 中有 "repackage"关键字参数的好方法吗?

c++ - 如何在 Ruby 和另一种语言之间共享文本文件(或互斥量/信号量)?

c++ - 带有 std::execution::par_unseq 的 std::for_each 不适用于 GCC 但适用于 MSVC

c# - 改进 Azure 表存储更新插入

mysql - 我需要提供/删除哪些 MySQL 权限以防止 Rails 的 db :reset?

ruby-on-rails - 为什么Refinery CMS创建记录后没有图像?

java - 带信号量的多线程

c++ - 自动更新最大值

java - Hibernate 使用大量线程

multithreading - 如何计算多线程进程的总计算时间