normalian blog

Let's talk about Microsoft Azure, ASP.NET and Java!

BackgroundWorkerの復習

以前、.NET上でマルチスレッド処理をしたいときに、さらっと使ったBackgroundWorkerコンポーネントですが、復習の為にちょっと使ってみました。
うーん、やっぱりクライアントアプリを作る際にマルチスレッド処理をする場合には有効そうだなぁ。スレッドの開始、終了のAPIのみならず、割り込み系のAPIまできっちりと用意されてるので、このコンポーネント使う分にはマルチスレッドの敷居は大分さがりそう。まぁ、マルチスレッド自体の危険が大きいので、「敷居を下げた」だけと言えばそうなのかも知れませんが、マルチスレッドに対する敷居が下がったのは良いですね。

public class MainClass
{
    BackgroundWorker _worker = new BackgroundWorker();
    public BackgroundWorker Worker
    {
        get { return _worker; }
    }

    public static void Main(string[] args)
    {
        MainClass instance = new MainClass();

        //RunWorkerAsync実行時に実行されるメソッド
        instance.Worker.DoWork += new DoWorkEventHandler((e, arg) =>
        {
            Console.WriteLine("★DoWork Sleep開始");
            Thread.Sleep(2000);
            Console.WriteLine("★DoWork Sleep終了");
        });

        //DoWork完了時に実行されるメソッド
        instance.Worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler((e, arg) =>
        {
            Console.WriteLine("★RunWorkerCompleted");
        });

        //重い処理実行中の割り込み処理
        instance.Worker.ProgressChanged += new ProgressChangedEventHandler((e, arg) =>
        {
            Console.WriteLine("★ProgressChanged, ProgressPercentage={0}, UserState={1}",
                arg.ProgressPercentage, arg.UserState);
        });
        //ここをtrueにしないとBackgroundWorker#ReportProgressが実行できない
        instance.Worker.WorkerReportsProgress = true;

        //以下、メイン処理
        Console.WriteLine("RunWorkerAsync実行前 IsBusy = {0}", instance.Worker.IsBusy);
        instance.Worker.RunWorkerAsync();
        instance.Worker.ReportProgress(70, "message");
        Console.WriteLine("RunWorkerAsync実行中 IsBusy = {0}", instance.Worker.IsBusy);

        Console.WriteLine("メインは終わり");
        Console.Read();
    }
}
//RunWorkerAsync実行前 IsBusy = False
//RunWorkerAsync実行中 IsBusy = True
//メインは終わり
//★DoWork Sleep開始
//★ProgressChanged, ProgressPercentage=70, UserState=message
//★DoWork Sleep終了
//★RunWorkerCompleted

11/20に追記

伊藤さんが http://blogs.bitlan.net/ito/?p=74 で突っ込んで下さってます。

まずは、リソースの解放を明示的に行っていない点です。
BackgroundWorker を含む ComponentModel 系のオブジェクトを、デザイナを使わずに使用する時は、自分で Dispose メソッドを明示的に呼び出すか、usingブロックを使用して、リソースを解放する必要があります。

上記の問題点については自覚しておりませんでした。ComponentModel系のオブジェクトをデザイナを使わない場合には要注意です…。

次に、BackbroundWorker の目的についてです。
BackbroundWorker の主な目的は、マルチスレッドの敷居を下げることではなく、ユーザーインターフェイスの応答性を保ちながら、大量のデータ処理をバックグラウンド(別スレッド)で処理させることです。

上記についても解釈を間違っていました。私の話した用途でBackgroundWorkerを使用する事もあると思いますが、ComponentModelオブジェクトとして提供している時点で「UIの応答性を保つ」が主なミッションになると考えられます。

別途伊藤さんブログの内容を参照して頂けると助かります。