Hatena::Groupangelos

Angelos in Action RSSフィード

Fork me on GitHub
 | 

2009-07-24

Coroでのマルチスレッドプログラミングでもthread内で使うクラスは極力statelessにしたほうがいい

01:50 | Coroでのマルチスレッドプログラミングでもthread内で使うクラスは極力statelessにしたほうがいい - Angelos in Action を含むブックマーク はてなブックマーク - Coroでのマルチスレッドプログラミングでもthread内で使うクラスは極力statelessにしたほうがいい - Angelos in Action Coroでのマルチスレッドプログラミングでもthread内で使うクラスは極力statelessにしたほうがいい - Angelos in Action のブックマークコメント

co-operativeでもpreemptiveでも結局のところ状態を持つコードは相性が悪いなぁということ。OOPのように、状態をインスタンス変数にもっちゃうと本当に相性が悪い。

ただ、co-operativeスレッドでユーザーが明示的にスレッドを書き換える場合には、デバッグは大分しやすくなるというのはあるのだけれど、Coroの場合、一部はユーザーが明示的にスレッドをかえなくても、スレッドが切り替わっていくわけで、これはどこで切り替わるのかを正確に理解していないと、結局いつのまにか違うスレッドに処理がうつっているということになる。

複数のスレッドオブジェクトの状態を書き換えるようになると、デバッグが急に難しくなる。これは、Javaのときも同じで、JavaはDIを使うのと、DIするコードはStatelessにして、状態をパッシングして渡すというのが一部で流行りました。こうすると、マルチスレッド環境下でもコードの副作用が無く、難しいバグを生みにくいという利点があります。特に、非同期になるコードが多くなると、OOPオブジェクトに状態を保持するようにするのは本当に相性が悪い。タイミングによって状態がかわるケースが多くなればなるほど、難しいバグを埋め込む事になる。

オブジェクトの状態によってインタラクションが変わるようになると、その変わるタイミングがたくさんあるというだけで、デバッグがかなり難しくなってしまうから。自分が思うに、Coroを使うのであれば、Coroのスレッドの中で使うコードは極力ステートレスにして、スレッド間の影響もないものにしたほうが、問題はシンプルになるだろうなぁと。

Coroと一緒にCPANモジュールつかうには、相性わるいモジュールのほうが多そうだなぁと、CPAN::PackagerでCoroを使ってみて思うのでした...

Coroで極力スレッドセーフであることを意識しないように既存モジュールを使うには

Coroのスレッド内ではスレッド間で状態を共有するようなコードは極力さけて、既存モジュールを使う際もスレッド間で影響がないように毎回スレッド内でインスタンス生成してそのスレッド内だけで有効になるオブジェクトを使うなどの工夫が必要だろうなぁと。

そういう工夫である程度はthread safeかどうかを意識する必要性は減って、それでも問題がでるのはシングルトンなクラスやパッケージ変数などに状態をもつもので、そこは気をつけるしかないということだろうなぁと。

例えば、割とやってしまいがちだなぁと思ったのは、以下のようなコード。スレッド内で状態をもつクラスにアクセスしているケースなんですが、既存のコードをCoroで並列化しようなんてケースでは結構ありそうだなぁと(めっちゃ自分のことですが...)

has 'densuke' => (
   default => sub {
     Densuke->new;
   }
);

sub connect {
   my $self = shift;
   push @coro, async {
      $self->densuke->connect($COIL_DOMAIN);
   };
   join $_ for @coro;
}

こういうケースの回避策は、上にも書いたように、

sub densuke {
  Densuke->new;
}

と毎回スレッド内でインスタンスを作る事。

おまけ

# on_enter, on_leaveでthreadの切り替わりを検知できるらしいです!mala++。これがあれば、少し工夫できるところはあるかも。

# 現状、Coro::LWP + CPANPLUS::BackendでつかっているFile::Fetch(lwpで)がうまく動いてないんだけれど、結局File::Fetch側の問題なんだろうなぁと。これを調べないといけないんだけれど、これはCoroのスレッド下で発生する問題で結局何かしら既存モジュールをつかうには、threadsafeであることを意識しなきゃいけないなぁと。当たり前と言えば当たり前なんですが...。で、Guardなどでダウンロードするところを並列化しなければ、問題は発生しないはずですが、パラレルにダウンロードしたいところを並列化しないのだと意味がなくなってしまうと...

# 個人的な感覚ではPerlモジュールがthread safeであることを意識して作られているケースの方がすくないんじゃないかなぁと思っていて、既存モジュールをCoroスレッド内で使いだすと結構はまるんじゃないかなぁと思ったのでした。

# File::Fetchの問題は、今回のthread safeの話とは違う問題そうだなぁと。ただ、いつこのケースにあたっても不思議はないかなぁと。

 |