BLOGTIMES
2010/10/18

RubyのWin32OLEは現状でマルチスレッド対応ではないらしい

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

先日書いたエントリのサンプルには問題があって、スレッドは切り替わるもののWin32OLEの呼び出し部分でブロックしてしまうので、Ruby 1.9にしても本質的にはうまく実行できません。前回は待ち時間が短かかったのと、タイムスタンプを出力していなかったのでスケジューリングがめちゃくちゃなことに気づいていませんでした。

また、Ruby 1.9.2のWIN32OLEの欄にも以下の注意書きがあったのですが見落としていました。

class WIN32OLE

マルチスレッドでの利用制限
注)以下の記述はWIN32OLEの将来のバージョンの仕様を規定するものではありません。
WIN32OLEはシングルスレッドモードでCOMとインターフェイスします。このため、ruby 1.9以降のRubyのThreadとネイティブスレッドが1対1で対応する実行環境ではスレッドをまたがる呼び出しはエラーとなります。

ということで、現在のところWin32OLE内でブロックするプログラムをマルチスレッドでうまく動かす方法はないようです。Win32OLEマルチスレッド対応という話もあるようなので、もしかしたらもう少しで解決されるのかも知れないので、期待して待ちたいと思います。

テストコード

oletest.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("ReceiveTimeout" => 5000) # msg = q.Receive("ReceiveTimeout" => 1); sleep 5 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"

検証結果

上記のコードをそのまま実行した結果は下記の通り。
各スレッドの結果が5秒ごとに得られているのかと思ったのですが、まったくのデタラメでした。
この現象はブロックする時間が増えたり、スレッドが増えるほどどんどん酷くなります。

C:\tmp>ruby oletest.rb waiting... 2010-10-18 22:23:34 +0900:0:nil massage 2010-10-18 22:23:39 +0900:2:nil massage 2010-10-18 22:23:44 +0900:1:nil massage 2010-10-18 22:23:49 +0900:0:nil massage 2010-10-18 22:23:54 +0900:2:nil massage 2010-10-18 22:23:59 +0900:1:nil massage 2010-10-18 22:24:04 +0900:0:nil massage 2010-10-18 22:24:10 +0900:2:nil massage 2010-10-18 22:24:15 +0900:1:nil massage 2010-10-18 22:24:20 +0900:0:nil massage 2010-10-18 22:24:25 +0900:2:nil massage 2010-10-18 22:24:30 +0900:1:nil massage 2010-10-18 22:24:35 +0900:0:nil massage 2010-10-18 22:24:40 +0900:2:nil massage 2010-10-18 22:24:45 +0900:1:nil massage 2010-10-18 22:24:50 +0900:0:nil massage 2010-10-18 22:24:55 +0900:2:nil massage 2010-10-18 22:25:00 +0900:1:nil massage 2010-10-18 22:25:05 +0900:0:nil massage SIGINT trapped 2010-10-18 22:25:10 +0900:2:nil massage 2010-10-18 22:25:20 +0900:2:terminated! 2010-10-18 22:25:15 +0900:1:nil massage 2010-10-18 22:25:20 +0900:1:terminated! 2010-10-18 22:25:20 +0900:0:nil massage 2010-10-18 22:25:20 +0900:0:terminated!

コメントのアウトされている部分を有効化し、その上の行をコメント化してsleepをRuby側で行うようにすると、各スレッドでちゃんと5秒ごとに結果が取れるようになります。自分的にはこちらが意図した動きですが、これはRuby1.8でも実現できるのではないかと思います。

C:\tmp>ruby oletest.rb waiting... 2010-10-18 22:27:37 +0900:2:nil massage 2010-10-18 22:27:37 +0900:1:nil massage 2010-10-18 22:27:37 +0900:0:nil massage 2010-10-18 22:27:42 +0900:2:nil massage 2010-10-18 22:27:42 +0900:1:nil massage 2010-10-18 22:27:42 +0900:0:nil massage 2010-10-18 22:27:47 +0900:2:nil massage 2010-10-18 22:27:47 +0900:1:nil massage 2010-10-18 22:27:47 +0900:0:nil massage 2010-10-18 22:27:52 +0900:2:nil massage 2010-10-18 22:27:52 +0900:1:nil massage 2010-10-18 22:27:52 +0900:0:nil massage 2010-10-18 22:27:57 +0900:2:nil massage 2010-10-18 22:27:57 +0900:1:nil massage 2010-10-18 22:27:57 +0900:0:nil massage SIGINT trapped 2010-10-18 22:27:58 +0900:0:nil massage 2010-10-18 22:27:58 +0900:0:terminated! 2010-10-18 22:27:58 +0900:1:nil massage 2010-10-18 22:27:58 +0900:1:terminated! 2010-10-18 22:27:58 +0900:2:nil massage 2010-10-18 22:27:58 +0900:2:terminated! 26.520046sec.

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

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

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

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