永井@知能.九工大です.

Ruby/Tk のイベントループが CPU パワーを食い潰してしまう問題への
対処方法で困っています.

今の最新版 Ruby/Tk (というか tcltklib) でのイベントループ処理は,
Tcl_DoOneEvent に TCL_DONT_WAIT フラグを与えて
sleep しないようにした上で,処理対象イベントの有無に応じて
スレッドスイッチングを行うとうように実装しています.
これにより,Ruby/Tk と Ruby 本体のスレッドとを併用する際の
反応速度を向上させると同時に,この自前のイベントループを監視することで
Ruby/Tk でのデッドロック問題発生も回避するようにしています.

ですが,この方法の場合,確かに CPU を食い潰してしまうのが問題です.
Tcl/Tk のイベントループに依存していた旧バージョンでは
イベントが生じるまで sleep 状態に入りますから
その間は CPU 消費が抑えられる (タイマーでのスイッチングとなるため
動きが鈍くなります) のですが,新バージョンでは
力の限り働かせようとしてしまいます.
GUI 処理と同時に全力で処理しなければならないスレッドがあるのなら
全く問題はないのですが,そうでなければ無駄飯食いだというのも尤もです.

何とかしようと考えていたのですが,あちらを立てればこちらが立たずで,
なかなかいい考えが出ません.
いいアイディアがありましたら,ぜひお願いします.
気を付けねばならないのは次の点です.

・Tcl/Tk のバージョンに依存するような処理は許されない.
    Tcl/Tk の内部処理をいじれるなら問題解決の糸口にできそうなのですが,
    さすがにこの対処法はまずいですよね.バージョン間の互換性も考えると,
    せいぜい man Notifier で見れる程度の手続きまでしか使えないと思います.

・イベントループ監視スレッドは必要.
    デッドロックを回避するためには,監視スレッドを動かしている必要が
    あります.これが動きっ放しというのも CPU 負荷を上げている原因です.
    デッドロック原因となるのは Tcl/Tk 側から呼び出された Ruby 側の処理が
    さらに Tcl/Tk 側の処理を呼ぶ際ですから,その際に sleep している
    監視スレッドを起こすということも考えました.でも,呼び出しは多段に
    渡る可能性があることなどを考慮すると,逆に監視スレッドはいつ sleep 
    すればいいかということになって...(^_^;

・無駄な sleep は極力避けたい.
    反応が鈍くなる原因ですから当然ですよね.その意味では,順番待ちの
    スレッドの有無に応じたイベントループの待ち状態の変更ができれば
    いいのですが,これも簡単にはいきそうにありません.いつ新たな
    スレッドが作られるかわかりませんし,稼働中のスレッドがいつ sleep 
    状態にはいるかなどもわかりません.スレッドスケジューリングの際に
    イベントループ以外の順番待ちスレッドがあれば Tcl/Tk 側にタイマー
    イベントを送り込んで強制的にスレッドスイッチングを引き起こす
    というようなことも考えてみましたが,実現するには Ruby 本体にまで
    手を入れなければならないようでしたのでダメそうです.

# 他にもあったような気がしますが...

現状はお手上げ状態で,このままだと 2 種類のイベントループを用意して,
「うまく動く方を使ってね」というなさけない状態になりそうです.
自分の能力不足が情けない限りですが,ぜひとも支援をお願い致します.
-- 
                                         永井 秀利 (九工大 知能情報)
                                             nagai / ai.kyutech.ac.jp