运行 Ruby 中的多线程 Open3 调用
Running multi-threaded Open3 call in Ruby
我有一个大循环,我试图 运行 在线程中调用 Open3.capture3
而不是 运行 线性调用。每个线程应该 运行 独立并且在访问数据方面没有死锁。
问题是,线程版本太慢了,它占用了我的 CPU。
这里有一个线性规划的例子:
require 'open3'
def read(i)
text, _, _ = Open3.capture3("echo Hello #{i}")
text.strip
end
(1..400).each do |i|
puts read(i)
end
这是线程版本:
require 'open3'
require 'thread'
def read(i)
text, _, _ = Open3.capture3("echo Hello #{i}")
text.strip
end
threads = []
(1..400).each do |i|
threads << Thread.new do
puts read(i)
end
end
threads.each(&:join)
A时间比较:
$ time ruby linear.rb
ruby linear.rb 0.36s user 0.12s system 110% cpu 0.433 total
------------------------------------------------------------
$ time ruby threaded.rb
ruby threaded.rb 1.05s user 0.64s system 129% cpu 1.307 total
Each thread should run independently and there's no deadlock in terms of accessing data.
你确定吗?
threads << Thread.new do
puts read(i)
end
您的主题正在共享标准输出。如果你查看你的输出,你会发现你没有得到任何交错的文本输出,因为 Ruby 自动确保 stdout 上的互斥,所以你的线程有效地 运行 与一群没用的 construction/deconstruction/switching 浪费时间。
Ruby 中的线程仅在调用某些 Ruby 较少的上下文* 时才对并行有效。这样 VM 就知道它可以安全地 运行 并行,而线程不会相互干扰。看看如果我们只捕获线程中的 shell 输出会发生什么:
threads = Array.new(400) { |i| Thread.new { `echo Hello #{i}` } }
threads.each(&:join)
# time: 0m0.098s
与连续
output = Array.new(400) { |i| `echo Hello #{i}` }
# time: 0m0.794s
* 事实上,这取决于几个因素。一些虚拟机 (JRuby) 使用本机线程,并且更容易并行化。某些 Ruby 表达式比其他表达式更可并行化(取决于它们与 GVL 的交互方式)。确保并行性的最简单方法是 运行 单个外部命令,例如子进程或系统调用,这些通常是无 GVL 的。
我有一个大循环,我试图 运行 在线程中调用 Open3.capture3
而不是 运行 线性调用。每个线程应该 运行 独立并且在访问数据方面没有死锁。
问题是,线程版本太慢了,它占用了我的 CPU。
这里有一个线性规划的例子:
require 'open3'
def read(i)
text, _, _ = Open3.capture3("echo Hello #{i}")
text.strip
end
(1..400).each do |i|
puts read(i)
end
这是线程版本:
require 'open3'
require 'thread'
def read(i)
text, _, _ = Open3.capture3("echo Hello #{i}")
text.strip
end
threads = []
(1..400).each do |i|
threads << Thread.new do
puts read(i)
end
end
threads.each(&:join)
A时间比较:
$ time ruby linear.rb
ruby linear.rb 0.36s user 0.12s system 110% cpu 0.433 total
------------------------------------------------------------
$ time ruby threaded.rb
ruby threaded.rb 1.05s user 0.64s system 129% cpu 1.307 total
Each thread should run independently and there's no deadlock in terms of accessing data.
你确定吗?
threads << Thread.new do
puts read(i)
end
您的主题正在共享标准输出。如果你查看你的输出,你会发现你没有得到任何交错的文本输出,因为 Ruby 自动确保 stdout 上的互斥,所以你的线程有效地 运行 与一群没用的 construction/deconstruction/switching 浪费时间。
Ruby 中的线程仅在调用某些 Ruby 较少的上下文* 时才对并行有效。这样 VM 就知道它可以安全地 运行 并行,而线程不会相互干扰。看看如果我们只捕获线程中的 shell 输出会发生什么:
threads = Array.new(400) { |i| Thread.new { `echo Hello #{i}` } }
threads.each(&:join)
# time: 0m0.098s
与连续
output = Array.new(400) { |i| `echo Hello #{i}` }
# time: 0m0.794s
* 事实上,这取决于几个因素。一些虚拟机 (JRuby) 使用本机线程,并且更容易并行化。某些 Ruby 表达式比其他表达式更可并行化(取决于它们与 GVL 的交互方式)。确保并行性的最简单方法是 运行 单个外部命令,例如子进程或系统调用,这些通常是无 GVL 的。