それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

ruby スレッドの同時実行数を制御する

Ruby のスレッドをバンバン起動したい。

起動前に現在の実行本数を調べたり、Queue.push したりするの面倒すぎるので、もっと単純に制御したいとおもっていて、Threadにモンキーパッチを当てる方法が思いついた。

同時実行数を指定したスレッド化

#!/usr/bin/env ruby
# coding : utf-8
#スレッド条件を制限したい

class << Thread 
  alias_method :original_new, :new

  def Thread.new(*args, &block)
    if Thread.main[:max_concurrent] and Thread.main[:max_concurrent] > 0 then
      while(Thread.list.size >= Thread.main[:max_concurrent]) do
        Thread.pass
      end
    end  
    puts "start"
    Thread.original_new(args,&block)
  end
  def Thread.max_concurrent=(num)
    Thread.main[:max_concurrent]=num
  end
end

Thread.max_concurrent=5
threads = []
threads << Thread.new{|t|sleep 1}
threads << Thread.new{|t|sleep 1}
threads << Thread.new{|t|sleep 1}
threads << Thread.new{|t|sleep 1}
threads << Thread.new{|t|sleep 1}
threads << Thread.new{|t|sleep 1}
threads << Thread.new{|t|sleep 1}
threads.each{|t| t.join  }
puts :end

スレッド同時実行数の制御をすると何が嬉しいか

コマンドの大量起動とか便利じゃないですか?

./my_ruby_script *.jpg


などと、大量に画像を受け取って処理をする時に、Ruby側で処理をします。bashだと同時起動しないし。rubyですね。。やっぱ

他の方法だと面倒だ

  • 逐次処理していると遅い
  • とりあえず全部スレッド化すると重い
  • キュー使うコードは面倒

というわけで、なんか良い解決法を探してたら冒頭のやり方に行き着いた

一つ一つファイル処理を起動すると遅い

引数で受け取ったファイルを逐次処理していると遅い。

ARGV.each{|f|
  `mogriy -resize 1000 #{f}` 
  `mogriy -strip #{f}` 
}

じゃぁ連続大量起動は重い

だからといって、大量起動すると、ファイル数が100超えた時ひどく遅くなる。

ARGV.map{|f|
  Thread.new{
    `mogriy -resize 1000 #{f}` 
    `mogriy -strip #{f}` 
  }
}.each{|t| t.join}

じゃぁ、Queue 作って。。。

Queue使えば出来るよね。でも面倒だし、変数増えるし、処理が一貫しない。

#!/usr/bin/env ruby
#!/usr/bin/env ruby
require 'thread'
q = Queue.new
ARGV.each{|f| q.push f }
p = Proc.new{|f|
	until q.empty? do
		puts q.pop
		sleep 1;
	end
}

threads =[]
threads << Thread.new(&p)
threads << Thread.new(&p)
threads << Thread.new(&p)
threads << Thread.new(&p)
threads << Thread.new(&p)

threads.each{|t| t.join }

変数増えるし、Proc出てくるし、パッと見分からないし、あんまり嬉しくないですね。

なにより、rubyのThredってeachと相性悪く、each内でthread使うとコードが読みにくくなってですね。。

関連資料