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}
補講で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クラスに書いてある。