Multi-threading

定期的にマルチスレッド辛いという話を書いている気がする(前回)が、またそういう話です。

仕事のアプリはかなりマルチスレッドで、そのせいでしばしばスレッドまわりのバグがおこる。アプリケーションの性質上 compute intensive な部分や I/O (Binder) bound な部分がくまなくあるため、マルチスレッドを頑張っている事実はよい。しかしデザインが厳しい。

アプリは UI まわり以外はだいたいどこかの worker でコードが動く。そして UI 以外のコードは沢山ある。そうしたコード、というかクラスは thread-safe に書かれている。

でも thread-safe なコードを書くって大変じゃん?それよりは thread-safety というか concurrent access は一部の shared state 管理クラスにまかせ、あとは並列アクセスが発生しないように messaging based で作るほうがいいべ・・・。世の中サーバ側は割とそういう風になっている気がするのだが、クライアントサイドはなぜそうならないのだろうね。

前のプロジェクトは、所属するスレッドをあわらす context オブジェクトみたいのを持ち回るアーキテクチャを強いることでコードの実行スレッドを制限していた。Scala  だったら implicit parameter でやるようなことを手でやる雰囲気。これは機能していたが、一方でコードの古さから制限の仕方の筋が悪く、生産性は低かった。

オブジェクトの所属するスレッドを決める (広い意味で) actor 的なアプローチとは別に、Rx とかは状態をなるべく immutable にすることで thread-safety を実現している。これはこれでよい。アプリだとすべての状態を immutable にするのは現実的でない (Flux とかの連中はほっとくとしましょう) ので、Rx と actor をいい感じで使い分けて thread-safe なデザインを生み出してほしいものであることよ。

まあ世の中の大半のアプリのように background に追い出したいのが File I/O と Network プラスアルファくらいなら actor 的な部分はほとんど必要なく, Rx だけでよい。仕事のアプリはもうちょっと色々あるせいで話が単純でないから現状に至った結論を責める気はない。が、辛いことに代わりはない。


今のプロジェクトと前のプロジェクトに共通する問題。本来 thread-sanity をまあまあ実現できるデザインの意図があったことを読み取れる一方、その本来の意図が守られていない。architectural violation が発生しまくっている。そのせいで (たとえば thread context の持ち回りなど) 強制的な制限が残る一方で (immutability など) すり抜けやすい soft-constraint はすり抜けられ、バグがおこる。

これを architecture を守らないコードを書くやつが悪い、というのは簡単だけど、コードを書く側が守る気になれないようなデザインが悪いという方が自分の感覚に近い。デザインを決めた人 (TL とか) と下々のエンジニアの間に大きな実力差があり、かつ TL が micro-managing で下々が大人しい独裁的プロジェクトでは多少理不尽でも architectural constraint / contract を守り抜くことはできるけれど、もうちょっと下々がまともだと押し付けは成立しない。なので従うことにありがたみが感じられるようなデザインでないと生き残れない。そしてそういうデザインは、経験的には稀である。

のでかっこいいデザインを考えたい人は下々の気持ちになってがんばってちょ、と思うのだった。