重い処理を別スレッドで行う

雨で暇を持て余し、ExcelSheetExplorerに検索機能を実装とかしてみる。検索自体はファイルを順に舐めて比較するだけなのでたいしたことないんだけど、検索中アプリがフリーズしないように検索部分だけ別スレッドにしてマルチスレッド化しようとするとなかなかめんどいことになってきた。

フリーズを防止するだけならループ中でApplication.DoEvents()を使うとイベントキューを処理できるのでマルチスレッド化しなくても簡単に実現する技があったりする↓
時間のかかる処理の進行状況を表示する - .NET Tips (VB.NET,C#...)
でも、リンク先でも言及されてるようにレスポンスが悪かったり、イベント処理が完了するまで本来の重い処理がブロックされてしまったりなど問題がないわけではない。

というわけで、やっぱりスレッドでやろうといろいろ手動でやってたわけだが、最終的にわかったことに、.NET2.0からスレッドを自分で管理せんでも、重い処理を裏でやるという用途に特化したBackgroundworkerコントロールという素敵なコンポーネントが利用可能らしい。引数渡し、中断、ステータスバーとか用に進捗報告、結果の取得などをカプセル化。というわけでこちらを採用。詳しくはこのへん↓
時間のかかる処理をバックグラウンドで実行するには?[2.0のみ、C#、VB] - @IT

Backgroundworkerは便利で楽チンなのはよいのだけど、注意点が1つ。スレッドそのものの管理はいらんけど、内部的にはDoWorkEventのハンドラとなるメソッドは別スレッドで実行されているわけで、Formコントロールは基本的にスレッドセーフではないので、何も考えずに作業用の別スレッドからテキストボックスやリストビューにアクセスしたりすると例外で落ちる。別スレッドからスレッドセーフにFormコントロールにアクセスするにはマーシャリングという技が必要。詳しくはこのへん参照↓
http://codezine.jp/a/article.aspx?aid=139#form
要約すると、別スレッドからアクセスしたいForm関係の処理をコールするだけの関数をデリゲートとして作成し、Form.Invokeに渡すことで、フォームを所有しているスレッドから間接的にコールしてくれるようになる。このForm.Invoke自体はスレッドセーフが保証されていて、別スレッドから呼んでも問題ないようにできているという仕組み。

そんなこんなでめでたくマルチスレッド化できました。