mikutterのThreadによる並列処理の考え方
現在のRubyでは、複数のThreadによる並列処理をしても、単一のプロセッサで複数のThreadが時系列で切り替わるだけなので、本当の意味で並列処理されるわけではないし、処理が速くなるわけじゃあない。んで俺は頭が弱いので、並列処理なんてしたら基本的にぽぽぽぽーんしてしまう残念な頭をしているので、極力使わないようにしています。
!!!触らぬ神に祟なし!!!
ということですね。
積極的に使う数少ない例外ケースは、ネットワーク通信。サーバの応答待ちの間、シングルスレッドなら他の処理をブロックしてしまうけれど、他のスレッドでネットワーク通信をすればブロックされることはないと。
また、mikutter内ではメインスレッドでしかGtk関連の操作をしてはいけないことになっています。つまり
メインスレッド → UIの処理、すぐやるべき処理
その他スレッド → 通信など
という役割になっています。だからメインスレッドであんまり時間がかかる処理はしないように心がけましょう!
Twitter APIを叩くためのメソッドは、メインスレッドで呼ばれるとクラッシュするようになっています。又、Gtk関連の一部メソッドも、メインスレッド以外で呼び出すとクラッシュするようになっています。
並列・遅延処理のためのメソッドいろいろ
そんなmikutterのための並列処理などのためのお役立ちメソッド|クラスたち。
コンストラクタに渡したブロックをあとで実行する。あとでというのは、そのうちヒマな時にやるということで、そんなに即時性が重要じゃない処理に使う。
Delayer.new{
# Do something
}
ブロックは登録された順番に呼ばれる。また、必ずメインスレッドで呼ばれる。
Threadとほとんど同じだけど、別々のSerialThread同士は順番に実行され、同時に実行されることはない。Delayerだとメインスレッドをブロックしてしまい、UI操作に支障をきたす可能性がある処理につかう。一つのThreadを使いまわすので、Threadが増えない。
SerialThread.new{
# Do something
}
RubyでHaskellみたいな遅延評価をするためのもの。ちょっとへんなのもあるけど。
ブロックを渡して使う。戻り値は、ブロックの実行結果なんだけど、ちょっとブロックの実行するタイミングが違う。
lazy
遅延評価オブジェクトを返す。初めてこのオブジェクトにたいしてメソッドが呼ばれたときに、1度だけブロックを実行する。
a = lazy{ fact(156) }
b = lazy{ fact(39) }
p b
上の例だと、39の階乗が出力されるけれど、実際に計算されるのは表示されるとき。156の階乗も一見計算しているようだけど(a)、使われていないので計算されない。
everytime
lazyと殆ど同じだけど、メソッドが呼ばれるたびに毎回ブロックを実行する。
parallel
lazyは最初に必要になったときだけど、parallelはすぐに別のスレッドでブロックを1度だけ実行する。実際に値が必要になったときに、まだ実行が終わっていない場合は、ブロックの実行終了まで待つ。
atomic
グローバルなMonitorのインスタンスのロックを取得してからブロックを処理する。ひとつしかMutexがなければデッドロックなんてないんや原理主義者のために用意されている。コアで使うためにと思って用意しました
atomic{
# Do something
}
イベントが処理されるスレッド
一方、イベントフィルタは、Plugin.filteringを呼んだ時に直ちに実行されるので、どのスレッドから呼ばれるかは不定です。つまり、フィルタは特にマルチスレッド処理に注意!ということです。
まとめ
mikutterで処理のタイミングをずらすためのユーティリティをまとめました。また、スレッドの扱い方の暗黙のルールを明文化しました。