mikutterにおけるスレッドと並行処理機構のまとめ
mikutterの薄い本Vol.8に掲載したmikutter-datasource-aclogを入れてから、なんかたまにGUIが重たくなる気がして来ました。
原因を調べたところ、mikutterイベントon_period内のhttp通信処理がたまに重くなるのせいで、メインスレッドのGUIの動作を阻害していたためでした。
てっきり別スレッドで動いてると思ってたよ。あらあらうふふ。
いい機会なので、mikutterに於けるスレッドの取り扱いと、mikutterフレームワークの並列機構との関係を整理してみようと思いました。
mikutterに於けるスレッド
mikutterのスレッドは「メインスレッド」と「サブスレッド」に大別されます。
特徴はそれぞれこんな感じです。
mikutterフレームワークの並行処理機構
さて、各スレッドの得手不得手が整理できた所で、次はmikutterで使える並行処理について確認していきましょう。
Delayer
与えたブロック(処理)をメインスレッドで実行します。
DelayerはGTKのメインループと協調を取っており、GUI処理がひと段落するタイミングまで実行が保留される所がちょっとだけ注意です。
(/core/plugin/gtk/delayer.rbのGtk::idle_add_priority()で暇になった時にDelayerを実行する処理を設定しています。)
この性質を利用して、擬似的にサブスレッドからGUIの操作が行えるようになります。
Thread.new { # サブスレッドでなんか重い処理 Delayer.new { # メインスレッドでGUI操作 } }
Reserver
Reserverは引数に指定した時間待った後にブロックを実行する機構です。
Reserverは専用のスレッドで時間待ちをしているため、Reserver処理もサブスレッドでの実行されます。
つまり、GUIをいじる場合はDelayerを使用する必要が有ります。
# 30秒後に処理を実行する Reserver.new(30) { # ここはサブスレッドで実行される }
イベント
mikutterイベントは内部でDelayerが噛むので、必ずメインスレッドで実行されます。
データを定期的に取得するのに便利なon_period(1分周期で発生)も例外ではなく、そこに冒頭のhttp処理なんかを書くと「もっさり」の原因となります。
(冒頭のやらかしですね。)
サブスレッドを作ってさっさと明け渡しましょう。
# 1分周期イベント on_period { |service| # ここはメインスレッドなので、速やかに明け渡しましょう。 }
フィルタ
イベントと似た構文を持つフィルタは呼び出し元に値を返す必要があるため、処理が実行されるスレッドは呼び出し元と同じ(メイン、サブ特定できない)になります。
まぁ、フィルタ処理でスレッドやDelayerによる並行処理は原理上無意味なので、困ることはないと思います。
しかしながら、フィルタ内で重い処理を行うと後続の何らかの処理が遅れるはずなので、重い処理を伴うデータはReserverやon_periodなどで予め非同期に作っておくのが良いと思います。
on_period { |service| Thread { # 重い処理は予め実行しておく @data = heavy_proc() } } filter_hoge { |args| # フィルタでは@dataを使って答えを返すだけ(排他とかちゃんとしてね) [args] }