Ruby:等待所有线程完成使用 join 和 ThreadsWait.all_waits - 有什么区别?

Ruby: Wait for all threads completed using join and ThreadsWait.all_waits - what the difference?

考虑以下示例:

threads = []

(0..10).each do |_|
  threads << Thread.new do
    # do async staff there
    sleep Random.rand(10)
  end
end

完成后有两种等待方式:

  1. 使用连接:

    threads.each(&:join)
    
  2. 使用ThreadsWait:

    ThreadsWait.all_waits(threads)
    

这两种方式有什么区别吗?

我知道 ThreadsWait class 还有其他有用的方法。 并特别询问 all_waits 方法。

documentation 明确指出 all_waits 将在每个线程执行后执行任何传递的块; join 不提供此类服务。

require "thwait"

threads = [Thread.new { 1 }, Thread.new { 2 }]

ThreadsWait.all_waits(threads) do |t|
  puts "#{t} complete."
end # will return nil

# output:
# #<Thread:0x00000002773268> complete.
# #<Thread:0x00000002772ea8> complete.

要用 join 完成同样的事情,我想你必须这样做:

threads.each do |t|
  t.join
  puts "#{t} complete."
end # will return threads

除此之外,all_waits 方法最终会调用 join_nowait 方法,该方法通过调用 join 来处理每个线程。

没有任何块,我想直接使用 join 会更快,因为你会减少所有导致它的 ThreadsWait 方法。所以我试了一下:

require "thwait"
require "benchmark"

loops = 100_000
Benchmark.bm do |x|
  x.report do
    loops.times do
      threads = [Thread.new { 2 * 1000 }, Thread.new { 4 * 2000 }]
      threads.each(&:join)
    end
  end

  x.report do
    loops.times do
      threads = [Thread.new { 2 * 1000 }, Thread.new { 4 * 2000 }]
      ThreadsWait.all_waits(threads)
    end
  end
end

# results:
# user       system     total       real
# 4.030000   5.750000   9.780000  ( 5.929623 )
# 12.810000  17.060000  29.870000 ( 17.807242 )

使用 map 而不是 each,将等待它们,因为它需要它们的值来构建地图。

(0..10).map do |_|
  Thread.new do
    # do async staff there
    sleep Random.rand(10)
  end
end.map(&:join).map(&:value)