それマグで!

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

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

Rubyでマルチスレッド 10 # Worker-Thread

Ruby結城浩せんせいのJavaマルチスレッド本 その10 Worker-thread

worker-thread

  • thread-pool-
  • Background-thread

とも呼ばれる

woker の役割

  • 仕事が届いたら仕事する。
  • 仕事がなければ、仕事が来るまでまつ。

細かい制御をしてチューニングして使う。

Wokerを何本起動するかでメモリなどを上手に使う。
仕事量が増えてきたら、Wokerを動的に起動し、本数を増やす事も可能。
仕事量が減ってきたら、Wokerを終了して、本数を減らす。

rubyでかいた woker thread

#!/usr/bin/env ruby
#Worker threads を実装する
require "thread"
class WorkerThread
  def initialize name,channel
    @name = name
    @channel = channel
  end
  def start
    @thread = Thread.new{
      while true do
        req = @channel.pop_request
        req.execute
      end
    }
  end
end
class Channle
  MAX_REQUEST = 100
  def initialize max
    #キューはこの章の本質じゃないのでSizedQueueを使う
    @req_queue = SizedQueue.new(MAX_REQUEST)
    @max
    @thread_pool = (1..max).map{|i| w=WorkerThread.new("Worker-#{i}", self) }
  end
  def start_workers()   @thread_pool.each{|w| w.start} end
  def push_request(req) @req_queue.push(req);end
  def pop_request()     @req_queue.pop; end
 end
class Request
  def initialize name, num
    @name = name
    @num = num
  end
  def execute 
    puts Thread.current.to_s + " executes #{self.to_s}"
    sleep rand(999).to_f/10000
  end
  def to_s() "[ Request from #{@name} No. #{@num.to_s} ]";end
end
class ClientThread
  def initialize name,channel
    @name = name 
    @channel = channel
  end
  def start()
    t = Thread.new{
      10000.times{|i|
        req = Request.new @name, i
        @channel.push_request req
        sleep rand(999).to_f/1000
      }
    }
  end
end

channel = Channle.new 5
channel.start_workers
client_threads = [
  ClientThread.new("Alice", channel),
  ClientThread.new("Bobby", channel),
  ClientThread.new("Chris", channel),
  ].map{|c|t=c.start}
client_threads.each{|t|t.join}

Apacheが使ってる。

Apacheの設定で prefork と woker-thread が選べる。

補講でSwingが紹介されてた。

SWINGのイベントディスパッチャーが使ってる。EventソースがEventQueueにためて、EventDispatcherがEventListerを呼び出す感じ。EventQueueはサイズ制限がないのと、EventDispatcherはシングルスレッド。

ClientThread待ち。

Ruby実装例だと、Clientスレッドを待つ。コレが辛い。なぜ辛いか?Wokerが仕事を終える前に、Clientスレッド終了→メイン終了になる。

Producer-Consumerと紛らわしい

Taskの中身は知らない。←ココ重要

WokerはTableに置いたTaskを実行する。Taskの中身はexecuteメソッドがあれば何でも良い。軽量スレッドです。タスクの受け渡しにProducer-Consumerを使っている。ConsumerがWokerになる。データはTask。ProducerConsumerを使っているので紛らわしい。

Wokerは軽量スレッドで、実作業はRequestクラスに書いてある。