BLOGTIMES
2010/10/26

JRuby と IronRuby と Ruby-mingw32 のスレッド実装の違い

  ruby 
このエントリーをはてなブックマークに追加

先日からRubyのスレッドの挙動に振り回されている*1わけですが、これまでに試したのはmingw32版のv1.8.7とv1.9.2だけでした。結論としては、これらの実装からマルチスレッドでWin32OLEオブジェクトの呼び出しを行うと、プロセス全体がブロックしてしまうようです。

それではJavaで実装されているJRuby や、.NET Frameworkで実装されている IronRuby の実装はどうなっているのだろうという疑問が湧いたので、今日はこれらの実装で先日のプログラムを流して、これらがどのような挙動を示すのかについて調べてみました。結論から述べると、これらの実装はWin32OLEのようなネイティブのライブラリを呼び出しても、他のスレッドがブロックすることはありませんでした。本家版のRubyと、JRubyやIronRubyは厳密にはスレッドの挙動が異なっているようです。

JRuby編

まずはJRubyからです。

C:\>\jruby-1.5.3\bin\jruby --version jruby 1.5.3 (ruby 1.8.7 patchlevel 249) (2010-09-28 7ca06d7) (Java HotSpot(TM) Client VM 1.6.0_20) [x86-java]

JRubyにはWin32OLEが標準添付ではないので、まずgemを使ってjruby-win32oleをインストールしてやる必要があります。
jruby-win32oleではThe JACOB Project: A JAva-COM Bridge のJACOBというライブラリを使ってJavaとCOM間のブリッジをしているようです。これは何かのときのために覚えておくと便利そうです。

C:\>\jruby-1.5.3\bin\gem install jruby-win32ole JRuby limited openssl loaded. http://jruby.org/openssl gem install jruby-openssl for full support. Successfully installed jruby-win32ole-0.8.0 1 gem installed Installing ri documentation for jruby-win32ole-0.8.0... Installing RDoc documentation for jruby-win32ole-0.8.0...

早速、下記のプログラムを起動してみるとこんな感じで他のスレッドに邪魔されることなく動作しているのが分かります。
今回検証した中では僕が頭で思い描いた動作に最も近い動作をする実装です。

C:\>\jruby-1.5.3\bin\jruby jroletest.rb waiting... Tue Oct 26 17:08:59 +0900 2010:0:nil massage Tue Oct 26 17:08:59 +0900 2010:2:nil massage Tue Oct 26 17:08:59 +0900 2010:1:nil massage Tue Oct 26 17:09:04 +0900 2010:0:nil massage Tue Oct 26 17:09:04 +0900 2010:2:nil massage Tue Oct 26 17:09:04 +0900 2010:1:nil massage Tue Oct 26 17:09:09 +0900 2010:0:nil massage Tue Oct 26 17:09:09 +0900 2010:2:nil massage Tue Oct 26 17:09:09 +0900 2010:1:nil massage SIGINT trapped Tue Oct 26 17:09:14 +0900 2010:0:nil massage Tue Oct 26 17:09:14 +0900 2010:0:terminated! Tue Oct 26 17:09:14 +0900 2010:2:nil massage Tue Oct 26 17:09:14 +0900 2010:2:terminated! Tue Oct 26 17:09:14 +0900 2010:1:nil massage Tue Oct 26 17:09:14 +0900 2010:1:terminated! 20.845sec.

ちなみにテストプログラムはjruby-win32oleを使っているのでrequireの追加と、名前つきパラメータでのメソッドの呼び出しをサポートしていないので、メソッド呼び出し部分を変更しています。

jroletest.rb

start = Time.now require 'rubygems' require 'jruby-win32ole' require 'thread' module MSMQ; end WIN32OLE::const_load(WIN32OLE.new('MSMQ.MSMQQueueInfo'), MSMQ) QUEUE_NAME = 'test' MAX_THREAD = 3 @threads = ThreadGroup.new @active = true Signal.trap(:INT){ puts "SIGINT trapped\n" @active = false @threads.list.each{ |thread| thread.run } } MAX_THREAD.times do |n| t = Thread.new do mqi = WIN32OLE.new('MSMQ.MSMQQueueInfo') mqi.PathName = '.\private$\\' + QUEUE_NAME q = mqi.Open(MSMQ::MQ_RECEIVE_ACCESS,MSMQ::MQ_DENY_NONE) while @active msg = q.Receive(nil,nil,nil,5000) puts "#{Time.now.to_s}:#{n}:Label:#{msg.Label}, Body:#{msg.Body}, Priority:#{msg.Priority}\n" if msg puts "#{Time.now.to_s}:#{n}:nil massage\n" unless msg end puts "#{Time.now.to_s}:#{n}:terminated!\n" unless msg end @threads.add(t) end puts "waiting...\n" @threads.list.each{ |thread| thread.join } puts "#{Time.now - start}sec.\n"

IronRuby編

次はIronRuby。こちらは標準でwin32oleが使えるので、特に何もインストールする必要はありませんが、やはり名前つきパラメータでのメソッドの呼び出しをサポートしていないので、その部分については変更してあります。こちらもJRubyと同様にスレッドの動作が他のスレッドの影響を受けていないことが分かります。動き自体は問題ないのですが、なぜかCtr+CをちゃんとトラップできずにExceptionを吐いてしまいました。

C:\>ir --version IronRuby 1.1.1.0 on .NET 4.0.30319.1 C:\>ir iroletest.rb waiting... 2010-10-26 16:49:28 +0900:0:nil massage 2010-10-26 16:49:28 +0900:1:nil massage 2010-10-26 16:49:28 +0900:2:nil massage 2010-10-26 16:49:33 +0900:1:nil massage 2010-10-26 16:49:33 +0900:0:nil massage 2010-10-26 16:49:33 +0900:2:nil massage 2010-10-26 16:49:38 +0900:1:nil massage 2010-10-26 16:49:38 +0900:0:nil massage 2010-10-26 16:49:38 +0900:2:nil massage 2010-10-26 16:49:43 +0900:1:nil massage 2010-10-26 16:49:43 +0900:0:nil massage 2010-10-26 16:49:43 +0900:2:nil massage 2010-10-26 16:49:48 +0900:1:nil massage 2010-10-26 16:49:48 +0900:0:nil massage 2010-10-26 16:49:48 +0900:2:nil massage mscorlib:0:in `JoinInternal': Interrupt (System::Threading::ThreadAbortException) from bbb.rb:37:in `join' from bbb.rb:37 from bbb.rb:37:in `each' from bbb.rb:37

iroletest.rb

start = Time.now require 'win32ole' require 'thread' module MSMQ; end WIN32OLE::const_load(WIN32OLE.new('MSMQ.MSMQQueueInfo'), MSMQ) QUEUE_NAME = 'test' MAX_THREAD = 3 @threads = ThreadGroup.new @active = true Signal.trap(:INT){ puts "SIGINT trapped\n" @active = false @threads.list.each{ |thread| thread.run } } MAX_THREAD.times do |n| t = Thread.new do mqi = WIN32OLE.new('MSMQ.MSMQQueueInfo') mqi.PathName = '.\private$\\' + QUEUE_NAME q = mqi.Open(MSMQ::MQ_RECEIVE_ACCESS,MSMQ::MQ_DENY_NONE) while @active msg = q.Receive(nil,nil,nil,5000) puts "#{Time.now.to_s}:#{n}:Label:#{msg.Label}, Body:#{msg.Body}, Priority:#{msg.Priority}\n" if msg puts "#{Time.now.to_s}:#{n}:nil massage\n" unless msg end puts "#{Time.now.to_s}:#{n}:terminated!\n" unless msg end @threads.add(t) end puts "waiting...\n" @threads.list.each{ |thread| thread.join } puts "#{Time.now - start}sec.\n"

トラックバックについて
Trackback URL:
お気軽にどうぞ。トラックバック前にポリシーをお読みください。[policy]
このエントリへのTrackbackにはこのURLが必要です→https://blog.cles.jp/item/3869
Trackbacks
このエントリにトラックバックはありません
Comments
愛のあるツッコミをお気軽にどうぞ。[policy]
古いエントリについてはコメント制御しているため、即時に反映されないことがあります。
コメントはありません
Comments Form

コメントは承認後の表示となります。
OpenIDでログインすると、即時に公開されます。

OpenID を使ってログインすることができます。

Identity URL: Yahoo! JAPAN IDでログイン