From: Yuji Shigehiro Date: 1997-10-30T21:31:08+09:00 Subject: [ruby-list:5185] Re: listbox:curselection しげひろです. > Date: Tue, 28 Oct 97 10:11:48 +0900 > Subject: [ruby-list:5143] Re: listbox:curselection > Message-Id: <199710280105.KAA28108@picachu.netlab.co.jp> > ちなみに1.1b0ではtk.rbは重弘さんのtcltklibを使ったものになり > ます. うれしいです. 今の tcltklib の仕様のままだと, もっと嬉しいです (ruby のバージョンアップの時に毎回, 手で ext/ にコピーしなくて済むので...) > スクリプトの互換性は保たれますが,thread safeではなく > なります. 良く知らないのですが, thread safe とはどういうことなのでしょう??? ---- tcltk ライブラリ (tcltk.rb) や tcltklib ライブラリと thread を共存させ る方法について, いろいろやってみました. ruby の thread は, ruby インタプリタが task switch するのですが, TclTk.mainloop や TclTkLib.mainloop (すなわち Tk_MainLoop (n))を馬鹿正 直に呼ぶと, それらは ruby インタプリタに制御を戻さないので, それ以降 task switch しなくなってしまいます. 以下のスクリプトは, tcl/tk により, print ボタン(標準出力に ---- を表示 する) と exit ボタンを生成しますが, 同時に, 別 thread (以下, 子 thread と呼びます)で標準出力に * をひたすら書こうとします. が, 実行してみると, TclTk.mainloop に制御がわたってボタンが表示された 後は, 子 thread が止まります. print ボタンを何度かクリックすると, print "*" するときに ruby インタプリタに制御が戻るので, 時々 task switch しますが, これでは使えません. しかし, [1] の部分をコメントイン(?)すると, ちゃんと task switch するよ うになります. 何をしているかというと, tcl/tk のタイマを使って, 0.1 秒 毎に, (ruby インタプリタに実行される)コールバックの中から Thread.pass を呼んでいます. ただし, (少なくとも FreeBSD では) 別 thread がバンバン実行されるので, クリックの反応が凄く悪くなります. そこで [2] の部分をコメントインする と, まともに反応するようになります. (本文は, 更に下に続く.) ---- ここから ---- #! /usr/local/bin/ruby require "tcltk" # * を表示し続けるスレッド th1 = Thread.new { while TRUE print "*"; $stdout.flush # Thread.pass # [2] end } # おまじない ip = TclTkInterpreter.new() root = ip.rootwidget() $after, button, destroy, pack = ip.commands().indexes("after", "button", "destroy", "pack") # ボタンの生成と表示 c1 = TclTkCallback.new(ip, proc{print "------\n"}) b1 = TclTkWidget.new(ip, root, button, "-text print -command", c1) c2 = TclTkCallback.new(ip, proc{destroy.e(root)}) b2 = TclTkWidget.new(ip, root, button, "-text exit -command", c2) pack.e(b1, b2) #### [1] ここから #$idlecallback = TclTkCallback.new(ip, proc{ idletask() }) #def idletask() # Thread.pass # $after.e("100", $idlecallback) #end #$after.e("100", $idlecallback) #### [1] ここまで TclTk.mainloop() ---- ここまで ---- で, いろいろやっているうちに, どうやら, 無理に TclTk.mainloop (Tk_MainLoop (n)) を呼ぶ必要はなく, (tcl/tk の) update さえひたすら実 行して入れば良いのではないかと... 下のスクリプトは, [3] の部分で update し続けるのですが, ちゃんと動きま す. (ただし, Tk_MainLoop と違って, root ウィジェットを destroy しても, イベントループから抜けてくれません.) これだと, 常に ruby インタプリタが制御を握っているので, tcl/tk のタイ マを使って面倒なことをする必要はありません. もし, tcl/tk の C ライブラリが再入可能(?)ならば, これは thread safe と いって良いのでしょうか? ---- ここから ---- #! /usr/local/bin/ruby require "tcltk" # * を表示し続けるスレッド th1 = Thread.new { while TRUE print "*"; $stdout.flush Thread.pass end } # おまじない ip = TclTkInterpreter.new() root = ip.rootwidget() button, pack, update = ip.commands().indexes("button", "pack", "update") # ボタンの生成と表示 c1 = TclTkCallback.new(ip, proc{print "------\n"}) b1 = TclTkWidget.new(ip, root, button, "-text print -command", c1) c2 = TclTkCallback.new(ip, proc{exit}) b2 = TclTkWidget.new(ip, root, button, "-text exit -command", c2) pack.e(b1, b2) #### [3] ここから while TRUE update.e() Thread.pass end #### [3] ここまで ---- ここまで ---- (次回の tcltk.rb の公開時には, このあたりの仕組みをなんとかしなければ...) ---- 重弘裕二 阪大情報処理教育センター (sigehiro@rd.ecip.osaka-u.ac.jp) 阪大工情報システム白川研 (sigehiro@ise.eng.osaka-u.ac.jp)