マルチスレッドは排他が大切
今日はどっぷりプログラミングネタです。
あるお客さんむけにProDHCP構築のサポートをしていて、DHCP負荷テストツールをお渡しして検討をしていただいているのですが、動かしていると死ぬんだけど・・・とお問い合わせをいただき、よーく調べてみると・・・マルチスレッドに関する問題でした。DHCP負荷テストツールは自分たちのデバッグ用ツールとして作ったもので、製品並みの品質は元々考えていなかったとはいえ、恥ずかしいミスでした。。
問題の箇所は、ログファイルのローテーションでした。ざっくり書くと、
ファイルサイズチェック関数{
・ファイルサイズをチェック
・指定サイズを超えていたら、ファイルポインタをクローズしてリネームしてから新しいファイルをオープン
}
という感じの機能です。ログ出力関数は、
ログ出力関数{
・ファイルサイズチェック関数をコール
・ログ出力
}
という感じです。
これがマルチスレッドで動くと、様々なスレッドからログ出力関数がランダムに呼び出されます。すると、ファイルサイズチェック関数をあるスレッドで呼び出していて、ローテーションがおきていると、ちょうどファイルをクローズしてリネームしているタイミングに他のスレッドでログ出力関数を呼び出した時におかしなことになってしまうのです。もちろんファイルサイズチェック関数ないでミューテックスなどを使って排他すれば直ります。
ゼロから作る時には意外と注意するので大丈夫なのですが、デバッグ用ツールなどは大抵そこら中からソースを流用して手っ取り早く作るので、シングルスレッド向けに作ったものを使ってしまってこんなことになってしまうのです。これがマルチスレッドの怖さです。
また、冷静に考えるとわかるのですが、マルチスレッドはこんな感じであちらこちらで排他が必要になります。標準ライブラリ内でもかなりの関数は排他が組み込まれます。排他は当然並列性を止めるわけですから、「マルチにすれば速くなる」とならなくなるケースも実は結構あるわけです。データベースなんかでも排他はボトルネックになりますね。
マルチスレッドは排他に気をつけよう、ということと、何でもかんでも並列処理が最適とはかぎらない、ということをざっくり書いてみました。